AutomatiserarSE.psm1
# # Module 'AutomatiserarSE' # # Created by: Ispep # # Generated on: 2017-03-11 # # Blog www.automatiserar.se # function Set-MjInfluxPostDataStructure(){ <# .Synopsis This changes the array to an string for InfluxDB Written by Ispep www.automatiserar.se .DESCRIPTION This function is used to build the data format that Influx requires * Hidden function, used by post data function .EXAMPLE # Set-MjInfluxPostDataStructure -DataToParse $("Temperature=11.23","Luftfuktighet=100") Returns: Temperature=11.23,Luftfuktighet=100 .EXAMPLE Another example of how to use this cmdlet #> [cmdletbinding()] param( [Parameter(Mandatory=$true)][array]$DataToParse ) begin { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Convert Array to string] :: $DataToParse" } process { # Create an empty string $tmp_influxdatainfo = "" foreach ($obj in $DataToParse){ Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Extract Info ] :: $obj" # verify that "," and spaces are not in the array provided if ($obj.Contains(",") -or $obj.Contains(" ")) { Write-Error "This character `",`" or Space is not allowed in `"influxdataInfo`"!" break } else { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Current Data ] :: $tmp_influxdatainfo" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ ADD Data ] :: $obj" $tmp_influxdatainfo += "$obj," } Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Data in tmp_influxdatainfo] :: $tmp_influxdatainfo" } } end { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Replace last `",`"] :: [ ToDo ] :: $tmp_influxdatainfo" $tmp_influxdatainfo = $tmp_influxdatainfo.Substring(0,$($tmp_influxdatainfo.LastIndexOf(","))) Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Replace last `",`"] :: [ Done ] :: $tmp_influxdatainfo" return $tmp_influxdatainfo } } function Create-MJinfluxArray(){ <# .Synopsis Build an array of objects, this data can be used in the Send-MJInfluxData Written by Ispep www.automatiserar.se .DESCRIPTION This function will get an array of data, it will try to parse it with the following structure: It will replace all spaces with "_", this to avoid influxdatabase post structure. row1 : maintag row2 : taginfo row3 : value row4 : datetime "Maintag||||IP=10.20.30.40,Firmware=3.2||||WifiSignal=-22,Temperature=22.3,Laddning=4.2,AntalMS=1232||||2016-08-16 13:06:22.131" This is an exampel the data will be changed: ESP8266-NR12||||IP=10.20.30.40,Firmware=2.1||||WifiSignal=-89,Temperatur=24.94,Laddning=4.25,AntalMS=4213||||2016-08-16 13:06:22.131 .EXAMPLE Send-MJInfluxData -InfluxServer YourComputer -databaseName Automatiserarse -BulkData $($ArrayPostData) This is the values in the $ArrayPostData: ESP8266-NR12||||IP=10.20.30.40,Firmware=2.1||||WifiSignal=-84,Temperatur=12.81,Laddning=0.03,AntalMS=2456||||2016-08-18 02:06:11.621 ESP8266-NR12||||IP=10.20.30.40,Firmware=2.1||||WifiSignal=-84,Temperatur=12.88,Laddning=0.03,AntalMS=2482||||2016-08-18 02:11:04.365 .EXAMPLE Another example of how to use this cmdlet #> [cmdletbinding()] param( [Parameter(Mandatory=$true)]$DataToProcess, [int]$MaxProcessLimit = 5000 # The default limit for an array post to Influxdb default ) begin { Write-Verbose "[$($MyInvocation.InvocationName)]::[ Function version 1.0 ]" Write-Verbose "[$($MyInvocation.InvocationName)]::[ Number of rows] $($DataToProcess.count)" [array]$tmp_returnObject = @() # this will be returned later } process { if ($DataToProcess.count -gt $MaxProcessLimit){ Write-Verbose "[$($MyInvocation.InvocationName)]::[ More then the default array size! ]" Write-Error "This array of data is to big for InfluxDB default (more then $MaxProcessLimit) your data was $($DataToProcess.count), use `"MaxProcessLimit`" if you have increased this number in your database" break } else { Write-Verbose "[$($MyInvocation.InvocationName)]::[ Array are smaler than then the limit] $MaxProcessLimit" } # begin to process data an change the structure to the allowed data format. # Row 1 : until "||||" Anything until the first |||| will be "MainTag" "MyComputername|||| # Row 2 : Until "||||" Untill the second is found in the string the values vill be added as TagInfo "info=yes","moreInfo=no"|||| # Row 3 : Until "||||" When the third is found the data vill be added as values" "temperature=22.3","sun=22"|||| # Row 4 : utill "||||" after the forth it will try to convert the value to unix time format "2016-08-16 13:06:22.131" Write-Verbose "[$($MyInvocation.InvocationName)]::[ Try to split data ]" foreach ($tmprad in $DataToProcess){ if ($tmprad -match '(?<Mantag>.*)\|\|\|\|(?<Taginfo>.*)\|\|\|\|(?<values>.*)\|\|\|\|(?<date>.*)'){ Write-Verbose "[$($MyInvocation.InvocationName)][ Found Row ]" $Matches.date = "$($(get-date $($([datetime]$Matches.date).AddHours(-1)) -UFormat %s).Split(",")[0])000000000" $tmp_string = "$($Matches.Mantag),$($matches.taginfo) $($Matches.values) $($matches.date)`n" $tmp_returnObject += $tmp_string Write-Verbose "[$($MyInvocation.InvocationName)] $tmp_string" } else { Write-Warning "Unable to find a match for the row $tmprad" } } } end { return $tmp_returnObject } } function Send-MJInfluxData(){ <# .Synopsis Send data to InfluxDB databases Written by Ispep www.automatiserar.se .DESCRIPTION This function verify that your data is provided with the structure required by InfluxDB AVOID to use SPACE and "," those could make your input break. if data are posted ok, you will get "True" in response Current version does not allow for manual input of date! V1.1 - Added Bulkdata This will allow a maximum of 5000 objects to be posted to Influx. It requires that the time is added before and that $MainDataTag, TagInfo and Values is empty. othervise bulkdata is ignored .EXAMPLE # Send information to database Send-MJInfluxData -InfluxServer MyInfluxServer -databaseName Automatiserarse -MainDataTag "powershell" -TagInfo $("host=MyCompyter","Type=WIFI","OS=Windows10") -Value "value1=22.2,value2=12.4" True .EXAMPLE Another example of how to use this cmdlet #> [cmdletbinding()] param( [Parameter(Mandatory=$true)][string]$InfluxServer, [Parameter(Mandatory=$true)][string]$databaseName, [Parameter(Mandatory=$false)][string]$MainDataTag, [Parameter(Mandatory=$false)][array]$TagInfo, # example $("host=demo","ip=10.20.30.40") [Parameter(Mandatory=$false)][array]$Values, # example $("temperature=11","light=229") [parameter(Mandatory=$false)][array]$BulkData, # This allows 5000 objects to be written to the database. [switch]$https, [int]$port = 8086 ) begin { Write-Verbose "[$($MyInvocation.InvocationName):: [Function - Version 1.1]" # URL to post data to. [string]$tmp_httpmode = (set-mjInfluxhttps -https $https) $tmp_baseUrl = "$($tmp_httpmode)://$($InfluxServer):$($port)/write?db=$($databaseName)" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ post URL ] :: $tmp_baseUrl" } process { # Create a list with "," to split objects $tmp_influxdatainfo = "" # Define this as array, this will be posted to Influx later $Tmp_UrlBody = @() # This is added in version 1.1 to support both bulk and singel input. if ($MainDataTag -ne $null -and $TagInfo -ne $null -and $Values -ne $null) { $tmp_influxdatainfo = Set-MjInfluxPostDataStructure -DataToParse $TagInfo Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Data att Posta ] :: $tmp_influxdatainfo" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Values provided ] :: $Values" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Singel data post ] :: $Tmp_UrlBody" # Remember to use the last " " after to let Influx set the time! $Tmp_values = Set-MjInfluxPostDataStructure -DataToParse $Values Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Values returned ] :: $Tmp_values" $Tmp_UrlBody = $("$($MainDataTag),$tmp_influxdatainfo $($Tmp_values.trim()) ") } # if there is more than one row in bulkdata this will be used. elseif($BulkData.count -gt 1) { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ bulkdata ] :: $($bulkdata.Count)" $Tmp_UrlBody = Create-MJinfluxArray($BulkData) } else { Write-Error "Data provided did not match any post filter! You need to provide `"bulkdata`" with an array OR Maindatatag, TagInfo and Values with data" break } # Post data to InfluxDB try { $tmp_result = Invoke-RestMethod -Uri $tmp_baseUrl -Method Post -Body $Tmp_UrlBody return $true } catch { Write-Warning $($Error[0].ErrorDetails.Message) Write-Error "unable to send your data to database $databaseName" return $false } } end { } } function Drop-mjInfluxDB(){ <# .Synopsis Drops an InfluxDB Database Written by Ispep www.automatiserar.se .DESCRIPTION This command will drop an array of INFLUX databases. It will first check if the database exist. it supports database arrays if needed. .EXAMPLE # Drop-mjInfluxDB -InfluxServer MyInfluxServer -databaseName "Automatiserarse","demodemo" DatabasesToRemove : {Automatiserarse, demodemo} RemovedItems : {Automatiserarse, demodemo} RemovedItemCount : 2 Version : 1 HttpMode : http NotFoundDatabases : {} .EXAMPLE # Drop-mjInfluxDB -InfluxServer MyInfluxServer -databaseName "Automatiserarse","demodemo" -confirmRemove -Verbose DatabasesToRemove : {Automatiserarse, demodemo} RemovedItems : {Automatiserarse, demodemo} RemovedItemCount : 2 Version : 1 HttpMode : http NotFoundDatabases : {} #> [cmdletbinding()] param( [Parameter(Mandatory=$true)][string]$InfluxServer, [Parameter(Mandatory=$true)][array]$databaseNames, [bool]$https, [int]$port = 8086, [switch]$confirmRemove ) begin { # Main functions Version, this is returned from the function with the "Version" label $tmp_Drop_mjInfluxDBVersion = 1 Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Will try to drop ] $($databaseNames)" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Check http mode ]" [string]$tmp_httpmode = (set-mjInfluxhttps -https $https) # Url to use when removing databases $tmp_baseUrl = "$($tmp_httpmode)://$($InfluxServer):$port/query" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Base URL ] :: $tmp_baseUrl" # This object will be returned $tmp_InfluxremovedObjects = New-Object psobject -Property ([ordered]@{ DatabasesToRemove = [array]$databaseNames RemovedItems = [array]@() RemovedItemCount = [int]0 Version = [int]$tmp_Drop_mjInfluxDBVersion HttpMode = [string]$tmp_httpmode NotFoundDatabases = [array]@() }) } process { # Check if the database exist Write-Verbose "[$($MyInvocation.InvocationName)] :: Try to get all current databases" $Tmp_CurrentDatabases = List-mjInfluxDB -InfluxServer $InfluxServer -https $https # If this value is false, there where a problem with the connection if (($Tmp_CurrentDatabases.Success) -eq $false) { Write-Error "Unable to connect to server $InfluxServer to find any databases" break } # Unless the command connects to the database this will not continue if ($Tmp_CurrentDatabases.Success){ Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Checked for databases ] :: OK" # check all databases in the array. foreach ($tmp_database in $databaseNames) { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Check if database exist ] :: $tmp_database " ### ### ### Check if databas where found before removing. ### $tmp_matchDB = $false # Check all databases foreach ($tmp_db in $Tmp_CurrentDatabases.Databases){ Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Check if database match ] $tmp_database | $tmp_db" if ($tmp_db -cmatch $tmp_database){$tmp_matchDB = $true} } ### found match - ready to remove. if ($tmp_matchDB){ Write-Warning "removing $tmp_database" if ($confirmRemove) { $tmp_true = $true # this will loop untill done [string]$tmp_answer = Read-Host "Remove database $tmp_db from $($InfluxServer)? Y/N:" # Loop until Y / N is answerd while ($tmp_true) { if ("N" -eq $($tmp_answer)) { Write-Warning "Will not remove $tmp_database, breaking out" break } elseif("Y" -eq $($tmp_answer)) { $tmp_true = $false } else { [string]$tmp_answer = Read-Host "Wrong answer! Remove database $tmp_database from $($InfluxServer)? Y/N:" } } } try { $tmp_result = Invoke-RestMethod -Uri $tmp_baseUrl -Method Post -Body $("q=DROP DATABASE $($tmp_database)") $tmp_InfluxremovedObjects.RemovedItems += $tmp_database } catch { Write-Error "Something did not work ok! will not remove anymore databases" break } } # No databases where found with the name provided else { Write-Warning "Unable to find any databases on $InfluxServer with the name `"$tmp_database`"" $tmp_InfluxremovedObjects.NotFoundDatabases += $tmp_database } } } else { Write-Warning "No Databases where found on the server `"$InfluxServer`", breaking the function" break } } end { $tmp_InfluxremovedObjects.RemovedItemCount = $($tmp_InfluxremovedObjects.RemovedItems).count return $tmp_InfluxremovedObjects } } function List-mjInfluxDB(){ <# .Synopsis This will show all influx databases. Written by Ispep www.automatiserar.se .DESCRIPTION With this function an array with all databases and the status of the command is returned. To verify if the command where successful check the bool flag "Success". .EXAMPLE List-mjInfluxDB -InfluxServer MyInfluxServer Returns: Databases : {_internal, Automatiserarse} Server : MyInfluxServer HttpMode : http Version : 1 Success : True .EXAMPLE List-mjInfluxDB -InfluxServer NotAComputer Returns: Databases : {} Server : NotAComputer HttpMode : https Version : 1 Success : False .EXAMPLE List-mjInfluxDB -InfluxServer MyInfluxServer -https Returns: Databases : {_internal, Automatiserarse} Server : spelburken HttpMode : https Version : 1 Success : True #> [cmdletbinding()] param( [Parameter(Mandatory=$true)]$InfluxServer, [bool]$https = $false, [int]$port = 8086 # ) begin { # Change to http or https. [string]$tmp_httpmode = (set-mjInfluxhttps -https $https) Write-Verbose "[$($MyInvocation.InvocationName)] :: [ HTTP Mode ] :: $tmp_httpmode" # Create a Object $Tmp_Influxinfo = New-InfluxDBObject $Tmp_Influxinfo.Server = $InfluxServer $Tmp_Influxinfo.HttpMode = $tmp_httpmode } process { # Build base URL $tmp_posturl = "$($tmp_httpmode)://$($InfluxServer):$($port)/query?pretty=true" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ POST URL ] :: $tmp_posturl" # Try to get info about Influx databases try{ $Tmp_Influxinfo.Databases = $(Invoke-RestMethod -Uri $tmp_posturl -Method Post -Body $("q=SHOW DATABASES")).results.series.values $Tmp_Influxinfo.Success = $true } catch{ # If issues where detected try to provide help if ($(($Error[0].CategoryInfo).Reason) -eq "WebException") { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ ERROR WebException ] :: Verify connection on specified port and protocoll" Write-Warning "Error connecting to $tmp_posturl" } else { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ ERROR Unknown ] :: This issue is not yet registred in my function" Write-Warning "This issue is new.. no info about it :(" Write-Warning "Error connecting to $tmp_posturl" } } } end { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ END ] :: returning info, found $(($Tmp_Influxinfo.Databases).count) databases" return $Tmp_Influxinfo } } function set-mjInfluxhttps(){ <# .Synopsis change to Http or https written by Ispep www.automatiserar.se .DESCRIPTION hidden function.. .EXAMPLE # to use https set this value to true set-mjInfluxhttps -https $true #> [cmdletbinding()][OutputType([String])] param( [Parameter(Mandatory=$true)][bool]$https = $false # unless no info is provides this is default False ) begin { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ HTTP or HTTPS ] :: $https" } process { if ($https) { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Selected ] :: HTTPS " return "https" } else { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Selected ] :: HTTP" return "http" } } end { } } function New-mjInfluxDB(){ <# .Synopsis This function will create new InfluxDB Written by Ispep www.Automatiserar.se .DESCRIPTION This function checks if an influxDB exist before writting it to the server if an database exist you will be promted with this warning and no database will be created: WARNING: A database with the name "Automatiserarse" exists! will not create a new one .EXAMPLE # New-mjInfluxDB -databaseName "Automatiserarse" -InfluxServer MyInfluxServer Returns: Databases : {_internal, Automatiserarse} Success : True Server : MyInfluxServer Version : 1 HttpMode : http NewDatabase : Automatiserarse .EXAMPLE new-mjInfluxDB -databaseName "Automatiserarse" -InfluxServer MyInfluxServer -https $true -port 8086 Returns: Databases : {_internal, Automatiserarse} Success : True Server : MyInfluxServer Version : 1 HttpMode : http NewDatabase : Automatiserarse #> [cmdletbinding()] param( [Parameter(Mandatory=$true)][string]$InfluxServer, [Parameter(Mandatory=$true)][string]$databaseName, [int]$port = 8086, [bool]$https ) begin { # Version of the script [string]$tmp_httpmode = (set-mjInfluxhttps -https $https) # Create an object to return. $tmp_NewDbObject = new-InfluxDBObject $tmp_NewDbObject | Add-Member -MemberType NoteProperty -Name "NewDatabase" -Value $databaseName $tmp_NewDbObject.Server = $InfluxServer $tmp_NewDbObject.HttpMode = $tmp_httpmode # Unless this is set to false, no database will be created [bool]$tmp_dbExist = $false Write-Verbose "[$($MyInvocation.InvocationName)] :: [ HTTP Mode ] :: $tmp_httpmode" # URL to post data to $tmp_baseURL = "$($tmp_httpmode)://$($InfluxServer):$($port)/query" Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Base URL ] :: $tmp_baseURL" } process { # Check if the databases exist. Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Check if database exist ]" $tmp_currentDatabases = List-mjInfluxDB -InfluxServer $InfluxServer -https $https -port $port foreach ($tmp_db in $tmp_currentDatabases.Databases){ Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Check if name matches] $tmp_db vs $databaseName" if ($tmp_db -cmatch $databaseName){ Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Name occupied ! ] " Write-Warning "A database with the name `"$tmp_db`" exists! will not create a new one" $tmp_dbExist = $true break } else { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ No match where found ]" } } if (!($tmp_dbExist)){ Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Create Database ] :: [ Name ] :: $databaseName" try{ $tmp_result = Invoke-RestMethod -Method Post -Uri $tmp_baseURL -Body $("q=CREATE DATABASE $databaseName") # Check if the new database is listed $tmp_NewList = List-mjInfluxDB -InfluxServer $InfluxServer -https $https -port $port if ($($tmp_NewList.Databases.count) -gt $($tmp_currentDatabases.Databases.count)){ $tmp_NewDbObject.Databases = $tmp_NewList.Databases $tmp_NewDbObject.Success = $true return $tmp_NewDbObject } else { $tmp_NewDbObject.Databases = $tmp_NewList.Databases $tmp_NewDbObject.Success = $false return $tmp_NewDbObject } } catch { Write-Error "Something went wrong!" break } } } end { } } function New-InfluxDBObject(){ <# .Synopsis Create base objects for Influx functions Written by Ispep www.automatiserar.se .DESCRIPTION This function will return a base object for all Influx functions .EXAMPLE # New-InfluxDBObject Returns: Databases : {} Success : False Server : Version : 1 HttpMode : #> [cmdletbinding()] param( ) begin { Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Create Influx Objects ] " $Tmp_List_mjInfluxDBVersion = 1 # current version of the function } process { # This object is used when databases are loaded. Write-Verbose "[$($MyInvocation.InvocationName)] :: [ Create a database object list ] " $Tmp_Influxdbobject = New-Object psobject -Property ([ordered]@{ Databases = [array]@() Success = [bool]$false Server = [string]"" Version = [int]$Tmp_List_mjInfluxDBVersion HttpMode = [string]"" }) return $Tmp_InfluxDBObject } end { } } function Get-MjVeraMode { <# .Synopsis Read current mode from Vera Controller with UI7, returns an object current mode. .DESCRIPTION This will allow you to verify what mode your Vera currently running in. Created by Ispep Added 2016-06-06 WWW.Automatiserar.se .EXAMPLE Get-MjVeraMode -VeraIP Vera Value Mode ----- ---- 1 Home .EXAMPLE Get-MjVeraMode -VeraIP Vera -RequireLogin -VeraCredential (Get-Credential) Value Mode ----- ---- 1 Home .NOTES This component requires you to have a Vera controller. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$VeraIP, # Provide the IP or DNS name to your Vera Controller. [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [System.Management.Automation.PSCredential]$VeraCredential ) if (!(Test-Connection $VeraIP -Count 1)){Write-Warning "Unable to connect to IP / name: $VeraIP!, will not continue"; break} # Gather information about what mode Vera currently running in. if ($RequireLogin) { # Beginning to check what mode the controller is in, this function will use username and password. Write-Verbose "$($MyInvocation.InvocationName):: Requesting data with this username: $($VeraCredential.UserName)" $VeraMode = Invoke-WebRequest -Uri "http://$($VeraIP):3480/data_request?id=variableget&Variable=Mode" -Credential $VeraCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: Requesting data Requesting data without username" $VeraMode = Invoke-WebRequest -Uri "http://$($VeraIP):3480/data_request?id=variableget&Variable=Mode" } if ($VeraMode.StatusCode -eq 200) { # If the HTTP code returns 200 the command was successful Write-Verbose "$($MyInvocation.InvocationName):: HTTP code returned was 200, the command was successful" $tmpModeValue = switch (($Veramode.Content.trim())) { '1' {"Home" ; break} '2' {"Away" ; break} '3' {"Night" ; break} '4' {"Vacation"; break} Default {"ERROR" } } $VeraModeResult = New-Object -TypeName psobject $VeraModeResult | Add-Member -MemberType NoteProperty -Name "Value" -Value ($Veramode.Content.trim()) $VeraModeResult | Add-Member -MemberType NoteProperty -Name "Mode" -Value $tmpModeValue return $VeraModeResult } else { Write-Verbose "$($MyInvocation.InvocationName):: This command returned other other code than 200, this can indicate that the Vera controller has a new URL" Write-Warning "Did not get the response code 200, got this code: $($VeraMode.StatusCode)" } } function Send-MJSpeak { <# .Synopsis Send voice message to speaker. .DESCRIPTION This will make your computer read information and tell it with your speakers. Created by Ispep WWW.Automatiserar.se .EXAMPLE Send-MJ-Speak -message "Hello, its 4 degree Celsius outside today" .EXAMPLE Send-MJ-Speak -message "Hello, its 4 degree Celsius outside today" - .OUTPUTS Bool value are returned. .NOTES This component can be used to read information from systems. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] [OutputType([bool])] Param ( # This contains the message that you want to read. [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string] $Message, # Specify The volume you want to use [AllowNull()] [ValidateRange(0,100)] [int] $Volume = 100 # Default volume unless other value is provided ) Begin { } Process { $speakMessage = Add-Type -AssemblyName System.speech $speakMessage = New-Object System.Speech.Synthesis.SpeechSynthesizer $speakMessage.Volume = $Volume $speakMessage.Speak($message) if ($?){ return $true } else { return $false } } End { } } function Read-MJImageRGBPixel { <# .Synopsis Read colour information from defined areas in images, returns RGB info about the area .DESCRIPTION Read colour information from defined areas in images, returns RGB info about the area Provide a file path to an image and �X� and �Y� location In the image, this will return an RGB colour code from that area. It�s possible to provide a pixel area to. Default an average info from pixels will be 5 pixels +- the selected pixel. Created by: Ispep Date: 2016-04-20 Blog: www.Automatiserar.se .EXAMPLE This will find the RGB colour in location X22 and Y33 Read-MJImageRGBPixel -FilePath "C:\temp\DemoBild2.jpeg" -PixelX 22 -pixelY 43 FilePath : C:\temp\DemoBild2.jpeg PixelX : 22 PixelY : 43 PixelArea : 5 Success : True ImageWith : 3200 ImageHeight : 1080 Red : 247 Red_Max : 254 Red_Avg : 234 Red_Min : 188 Green : 250 Green_Max : 254 Green_Avg : 240 Green_Min : 188 Blue : 252 Blue_Max : 253 Blue_Avg : 246 Blue_Min : 188 ScriptVersion : 1 .EXAMPLE Read-MJImageRGBPixel -FilePath C:\Temp\DemoBild2.jpg -PixelX 22 -pixelY 43 -PixelArea 2 FilePath : C:\Temp\DemoBild2.jpeg PixelX : 22 PixelY : 43 PixelArea : 2 Success : True ImageWith : 3200 ImageHeight : 1080 Red : 247 Red_Max : 247 Red_Avg : 243 Red_Min : 239 Green : 250 Green_Max : 250 Green_Avg : 247 Green_Min : 245 Blue : 252 Blue_Max : 252 Blue_Avg : 251 Blue_Min : 251 ScriptVersion : 1 #> [cmdletbinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-las-rgb-information-ur-bilder/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true)][string]$FilePath, [int]$PixelX, [int]$pixelY, [int]$PixelArea = 5 #Provide the area around the pixels you want to know the average from. ) begin { # Create base object. $scriptversion = 1 $PixelObjekt=[ordered]@{ FilePath = $([string]$FilePath) PixelX = $([int]$PixelX) PixelY = $([int]$pixelY) PixelArea = $([int]$PixelArea) Success = $([bool]$true) ImageWith = $([int]) ImageHeight = $([int]) Red = $([int]) Red_Max = $([int]) Red_Avg = $([int]) Red_Min = $([int]) Green = $([int]) Green_Max = $([int]) Green_Avg = $([int]) Green_Min = $([int]) Blue = $([int]) Blue_Max = $([int]) Blue_Avg = $([int]) Blue_Min = $([int]) ScriptVersion = $([int]$scriptversion) } $ImageInfo = New-Object -TypeName psobject -Property $PixelObjekt if(!(Test-Path $($ImageInfo.FilePath))){Write-Warning "Unable to find the image $($ImageInfo.ImageInfo)"; $ImageInfo.Success = $false;} } PROCESS{ if ($ImageInfo.Success){ Write-Verbose "$($MyInvocation.InvocationName):: Processing Image" Add-Type -AssemblyName System.Drawing $MyBitmapImage = [System.Drawing.Bitmap]::FromFile($ImageInfo.FilePath) $ImageInfo.ImageHeight = $MyBitmapImage.Height $ImageInfo.ImageWith = $MyBitmapImage.Width # Define max / min area to get the average $MinX = $PixelX - $PixelArea $MaxX = $pixelX + $PixelArea $Miny = $pixelY - $PixelArea $MaXy = $pixelY + $PixelArea Write-Verbose "$($MyInvocation.InvocationName):: MinX = $MinX, MaxX = $MaxX, MinY = $minY, MaxY = $MaXy" # Loading image information if ($MaxX -le $MyBitmapImage.Width -and $MaXy -le $MyBitmapImage.Height -and $MinX -ge 0 -and $minY -ge 0) { Write-Verbose "$($MyInvocation.InvocationName):: Selected pixels are within the image size" # This section will only run if selection was within the image area $xValue = $MinX $yValue = $Miny $summa = while ($xValue -le $MaxX -and $yValue -le $MaXy){ while ($xValue -le $MaxX) { $MyBitmapImage.GetPixel($xValue,$yValue) $xValue++ } $xValue = $MinX $yValue++ } $tmpImage = $MyBitmapImage.GetPixel($PixelX, $PixelY) $ImageInfo.Red = [int]$tmpImage.r $ImageInfo.Green = [int]$tmpImage.g $ImageInfo.Blue = [int]$tmpImage.b $ImageInfo.Red_Avg = [int]($summa.r | Measure-Object -Average | Select-Object -ExpandProperty Average) $ImageInfo.Green_Avg = [int]($summa.g | Measure-Object -Average | Select-Object -ExpandProperty Average) $ImageInfo.Blue_Avg = [int]($summa.b | Measure-Object -Average | Select-Object -ExpandProperty Average) $ImageInfo.Red_Max = [int]($summa.r | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum) $ImageInfo.Green_Max = [int]($summa.g | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum) $ImageInfo.Blue_Max = [int]($summa.b | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum) $ImageInfo.Red_Min = [int]($summa.r | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum) $ImageInfo.Green_Min = [int]($summa.g | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum) $ImageInfo.Blue_Min = [int]($summa.b | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum) $MyBitmapImage.Dispose() } else # Area selected was outside the image { Write-Warning "The provide X and Y are not within the images resolution" $ImageInfo.Success = $false } } else { Write-Verbose "$($MyInvocation.InvocationName):: Unable to find the file $($ImageInfo.imageinfo)" } } END { return $ImageInfo } } # end function Read-MJImageRGBPixel Function Get-MJVeraBackup{ <# .Synopsis Downloads a backup of your vera controller. .DESCRIPTION This will download an backup of your vera controller and save it as a tgz filese Created by Ispep Added 2016-06-07 WWW.Automatiserar.se .EXAMPLE Get-MJVeraBackup -VeraIP VERA -DestinationPath C:\temp\ Filepath FileName Size CreationTime -------- -------- ---- ------------ C:\temp\VeraBackup-2016-06-07-17_27_26.tgz VeraBackup-2016-06-07-17_27_26.tgz 517025 2016-06-07 17:27:26 .EXAMPLE Get-MJVeraBackup -VeraIP VERA -DestinationPath C:\temp\ -RequireLogin -VeraCredential (Get-Credential) Filepath FileName Size CreationTime -------- -------- ---- ------------ C:\temp\VeraBackup-2016-06-07-17_29_08.tgz VeraBackup-2016-06-07-17_29_08.tgz 517023 2016-06-07 17:29:08 .EXAMPLE Get-MJVeraBackup -VeraIP VERA -DestinationPath C:\temp\ -Verbose VERBOSE: Get-MJVeraBackup:: Beginning function: Download backup from VERA VERBOSE: Get-MJVeraBackup:: Data will be gathered from the following URL: http://VERA/cgi-bin/cmh/backup.sh VERBOSE: Get-MJVeraBackup:: VERA responding on ping VERBOSE: Get-MJVeraBackup:: File path "C:\temp\" exists VERBOSE: Get-MJVeraBackup:: Joining path and downloading data from vera VERBOSE: Get-MJVeraBackup:: Downloading backup from VERA without username or password VERBOSE: Get-MJVeraBackup:: Download successful, creating object and returning it to the pipe Filepath FileName Size CreationTime -------- -------- ---- ------------ C:\temp\VeraBackup-2016-06-07-17_30_50.tgz VeraBackup-2016-06-07-17_30_50.tgz 517023 2016-06-07 17:30:50 .NOTES This component requires you to have a Vera controller. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$VeraIP, # Provide the IP or DNS name to your Vera Controller. [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$DestinationPath, [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [System.Management.Automation.PSCredential]$VeraCredential, [string]$VeraBackupFileName = "VeraBackup" # Provide the beginning of the filename if �VeraBackup� does not meet your requirements. ) if ($RequireLogin.IsPresent){ if (($VeraCredential.UserName).Length -le 3){ Write-Error "No credential was provided!" break } else { Write-Verbose "$($MyInvocation.InvocationName):: Username $($VeraCredential.UserName) was ok" } } Write-Verbose "$($MyInvocation.InvocationName):: Beginning function: Download backup from $veraIP" $VeraPath = "http://" + $veraIP + "/cgi-bin/cmh/backup.sh" Write-Verbose "$($MyInvocation.InvocationName):: Data will be gathered from the following URL: $veraPath" if (Test-Connection $veraIP -Count 1 -ErrorAction SilentlyContinue) { Write-Verbose "$($MyInvocation.InvocationName):: $VeraIP responding on ping" ### Verify if the path where the file going to be saved exist if (Test-Path $DestinationPath) { Write-Verbose "$($MyInvocation.InvocationName):: File path `"$DestinationPath`" exists" } else { Write-Verbose "$($MyInvocation.InvocationName):: The path does not exist, will try to create the following path: $DestinationPath" New-Item -Path $DestinationPath -ItemType directory -ErrorVariable FileError | Out-Null if ($FileError) { Write-Warning "The function $($MyInvocation.InvocationName) was unable to create the path $DestinationPath, exiting function" return $error[0] break } } # Folder created or already existed ### Trying to download the backup from vera Write-Verbose "$($MyInvocation.InvocationName):: Joining path and downloading data from vera" $VeraWebData = New-Object System.Net.WebClient $VeraBackupFilename = "$VeraBackupFileName-$(Get-Date -UFormat %Y-%m-%d-%H_%M_%S).tgz" $TargetDestination = Join-Path -Path $DestinationPath -ChildPath $VeraBackupFilename IF ($RequireLogin) { Write-Verbose "$($MyInvocation.InvocationName):: Username was required, will try to use the account $($VeraCredential.UserName)" Write-Verbose "$($MyInvocation.InvocationName):: Will now try to download gzip file to destination $TargetDestination" $VeraWebData.Credentials = $VeraCredential $VeraWebData.Headers.Add([System.Net.HttpRequestHeader]::AcceptEncoding, "gzip") $VeraWebData.DownloadFile($veraPath, $TargetDestination) # If the download was successful a object with all information about the backup will be returned. if ($?){ Write-Verbose "$($MyInvocation.InvocationName):: Download successful, creating object and returning it to the pipe" $Tmp_BackupInfo = Get-ItemProperty $TargetDestination $Tmp_Result = New-Object -TypeName psobject $Tmp_Result | Add-Member -MemberType NoteProperty -Name "Filepath" -Value $TargetDestination $Tmp_Result | Add-Member -MemberType NoteProperty -Name "FileName" -Value $VeraBackupFilename $Tmp_Result | Add-Member -MemberType NoteProperty -Name "Size" -Value ($Tmp_BackupInfo.Length) $Tmp_Result | Add-Member -MemberType NoteProperty -Name "CreationTime" -Value ($Tmp_BackupInfo.CreationTime) return $Tmp_Result } else { Write-Warning "An error has occurred, something did not go successfully. The error given was:" return $error[0] } } ELSE { Write-Verbose "$($MyInvocation.InvocationName):: Downloading backup from $veraIP without username or password " $VeraWebData.Headers.Add([System.Net.HttpRequestHeader]::AcceptEncoding, "gzip") $VeraWebData.DownloadFile($veraPath, $TargetDestination) if ($?){ Write-Verbose "$($MyInvocation.InvocationName):: Download successful, creating object and returning it to the pipe" $Tmp_BackupInfo = Get-ItemProperty $TargetDestination $Tmp_Result = New-Object -TypeName psobject $Tmp_Result | Add-Member -MemberType NoteProperty -Name "Filepath" -Value $TargetDestination $Tmp_Result | Add-Member -MemberType NoteProperty -Name "FileName" -Value $VeraBackupFilename $Tmp_Result | Add-Member -MemberType NoteProperty -Name "Size" -Value ($Tmp_BackupInfo.Length) $Tmp_Result | Add-Member -MemberType NoteProperty -Name "CreationTime" -Value ($Tmp_BackupInfo.CreationTime) return $Tmp_Result } else { Write-Warning "An error has occurred, something did not go successfully. The error given was:" return $error[0] } } } else { Write-Verbose "$($MyInvocation.InvocationName):: Your $VeraIP did not respond to the ping" Write-Warning "Unable to connect to $veraIp, this will return an Error to the pipe" Write-Error "Unable to connect to $veraip" } } # End Function Get-MJVeraBackup Function Get-MJVeraStatus{ <# .Synopsis This function will extract all information from your Vera controller and create an objects. .DESCRIPTION This function is one of the important commands to extract all info from your Vera controller. It will collect, devices, alerts, rooms. It will merge two objects into the �Devices� class to get access to name and room more easy. The original device will be found under DeviceRaw Created by Ispep Added 2016-06-07 WWW.Automatiserar.se .EXAMPLE Get-MJVeraStatus -VeraIP vera Devices : @{} DeviceRaw : @{} DeviceVerbose : @{} Rooms : @{} Scenes : @{} Alerts : @{} LocalTime : 2016-06-07 23:22:00 N visible_devices : [int] sensors_not_tripped : [int] fwd1 : [string] fwd2 : [string] Server_Device : [string] Server_Device_Alt : [string] RA_Server : [string] RA_Server_Back : [string] Mode : [int] mode_change_mode : [int] mode_change_time : [int] breach_delay : [int] mode_change_delay : [int] skin : [string] temperature : [string] City_description : [string] country_pk : [string] model : [string] serial_number : [int] Version : [string] Device_Num_Next : [int] Scene_Num_Next : [int] gmt_offset : [int] InstalledPlugins : @{} usergeofences : @{} awayStateAllUserNotAtHome : [int] homeStateAnyUserAtHome : [int] users : @{} ScriptVersion : [int] .EXAMPLE Get-MJVeraStatus -VeraIP vera -RequireLogin -VeraCredential (Get-Credential) Devices : @{} DeviceRaw : @{} Rooms : @{} Scenes : @{} Alerts : @{} LocalTime : 2016-06-07 22:03:33 D visible_devices : 8 sensors_not_tripped : 4 fwd1 : *URL to veras service* fwd2 : *URL to veras service* skin : mios temperature : C model : MiCasaVerde VeraLite serial_number : *[int value]* Version : *1.7.760* ScriptVersion : 1 .NOTES This component requires you to have a Vera controller. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$VeraIP, # Provide the IP or DNS name to your Vera Controller. [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [System.Management.Automation.PSCredential]$VeraCredential ) $ModuleVersion = 1 # This value will be increased if and when this module gets new functions. if ($RequireLogin.IsPresent){ if (($VeraCredential.UserName).Length -le 3){ Write-Error "No credential was provided!" break } else { Write-Verbose "$($MyInvocation.InvocationName):: Username $($VeraCredential.UserName) was ok" } } if (!(Test-Connection $VeraIP -Count 1)){Write-Warning "Unable to connect to IP / name: $VeraIP!, will not continue"; break} $VeraUrl1 = "http://$($veraIP):3480/data_request?id=status" # url to Veras Device Data $veraUrl2 = "http://$($veraIP):3480/data_request?id=sdata" # url to Veras Device Names $veraUrl3 = "http://$($veraIP):3480/data_request?id=lu_user_data2" # url to Veras Device Verbose Info if ($RequireLogin) { # Beginning to check what mode the controller is in, this function will use username and password. Write-Verbose "$($MyInvocation.InvocationName):: Requesting data with this username: $($VeraCredential.UserName)" $VeradataDevices = Invoke-RestMethod -Uri $VeraUrl1 -Method Get -ContentType json -Credential $VeraCredential $VeradataNames = Invoke-RestMethod -Uri $veraUrl2 -Method Get -ContentType json -Credential $VeraCredential $veradataInfo = Invoke-RestMethod -Uri $veraUrl3 -Method Get -ContentType json -Credential $VeraCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: Requesting data Requesting data without username" $VeradataDevices = Invoke-RestMethod -Uri $VeraUrl1 -Method Get -ContentType json $VeradataNames = Invoke-RestMethod -Uri $veraUrl2 -Method Get -ContentType json $veradataInfo = Invoke-RestMethod -Uri $veraUrl3 -Method Get -ContentType json } # This object will be returned when the script is done. $ObjectToReturn = [ordered]@{ Devices = @() DeviceRaw = @() DeviceVerbose = @() Rooms = @() Scenes = @() Alerts = @() LocalTime = $VeradataDevices.LocalTime visible_devices = $VeradataDevices.visible_devices sensors_not_tripped = $VeradataDevices.sensors_not_tripped fwd1 = $VeradataNames.fwd1 fwd2 = $VeradataNames.fwd2 Server_Device = [string] Server_Device_Alt = [string] RA_Server = [string] RA_Server_Back = [string] Mode = [int] mode_change_mode = [string] mode_change_time = [string] breach_delay = [int] mode_change_delay = [int] skin = $VeradataNames.skin temperature = $VeradataNames.temperature City_description = [string] country_pk = [int] model = $VeradataNames.model serial_number = $VeradataNames.serial_number Version = $VeradataNames.version Device_Num_Next = [int] Scene_Num_Next = $veradataInfo.Scene_Num_Next gmt_offset = $veradataInfo.gmt_offset InstalledPlugins = @() usergeofences = @() awayStateAllUserNotAtHome = [int] homeStateAnyUserAtHome = [int] users = @() ScriptVersion = $ModuleVersion # This value is used to determine when a newer version installed } $MainStatus = New-Object -TypeName psobject -Property $ObjectToReturn foreach ($device in ($VeradataDevices.devices)){ $MainStatus.DeviceRaw += $device Write-Verbose "$($MyInvocation.InvocationName):: Searching for name om $($device.id)" $Tmp_VeraDevice = $VeradataNames.devices | Where-Object {$_.id -eq $device.id} Write-Verbose "$($MyInvocation.InvocationName):: Found matches with id $($device.id)" if ($($(($Tmp_VeraDevice).psobject.Properties).count) -ge 4){ # this function will connect the two objects into one. $Tmp_VeraDevice.psobject.Properties| Select-Object -Property "name", "value" | Where-Object {$_.name -ne "id"} | ForEach-Object { $tmp = $_ $device | Add-Member -MemberType NoteProperty -Name $($tmp.name) -Value $($tmp.value) -ErrorAction SilentlyContinue # this will ignore that some values are duplicated if (!($?)){Write-Verbose "[ISSUE] - Will not merge `"$($tmp.name)`" On ID $($Tmp_VeraDevice.id), The name conflicts with a current name. The value for status is: `"$($tmp.value)`""} } $MainStatus.Devices += $device } # End merge two devices } # End Foreach loop $MainStatus.Rooms = $VeradataNames.rooms $MainStatus.Scenes = $VeradataNames.scenes $MainStatus.Alerts = $VeradataDevices.alerts $MainStatus.DeviceVerbose = $veradataInfo.devices $MainStatus.InstalledPlugins = $veradataInfo.InstalledPlugins2 $MainStatus.Device_Num_Next = $veradataInfo.Device_Num_Next $MainStatus.users = $veradataInfo.users $MainStatus.Server_Device = $veradataInfo.Server_Device $MainStatus.Server_Device_Alt = $veradataInfo.Server_Device_Alt $MainStatus.RA_Server = $veradataInfo.RA_Server $MainStatus.RA_Server_Back = $veradataInfo.RA_Server_Back $MainStatus.mode_change_mode = $veradataInfo.mode_change_mode $MainStatus.mode_change_time = $veradataInfo.mode_change_time $MainStatus.City_description = $veradataInfo.City_description $MainStatus.country_pk = $veradataInfo.country_pk $MainStatus.awayStateAllUserNotAtHome = $veradataInfo.awayStateAllUserNotAtHome $MainStatus.homeStateAnyUserAtHome = $veradataInfo.homeStateAnyUserAtHome $MainStatus.breach_delay = $veradataInfo.breach_delay $MainStatus.mode_change_delay = $veradataInfo.mode_change_delay $MainStatus.usergeofences = $(try {$(ConvertFrom-Json $veradataInfo.usergeofences)} catch {Write-Verbose "$($MyInvocation.InvocationName):: No Geofence Info"}) $MainStatus.mode = $veradataInfo.Mode return $MainStatus } # end Get-MJVeraStatus function Get-MJUnixTimeToNormal{ <# .Synopsis This function is not public, it�s used in my modules to translate date time .DESCRIPTION This function will convert an int unix time to dateTime Created by Ispep Added 2016-06-07 WWW.Automatiserar.se .EXAMPLE Get-MJUnixTimeToNormal -UnixTime 1465335719 7 juni 2016 21:41:59 .EXAMPLE $myTime = Get-MJUnixTimeToNormal -UnixTime 1465335719 $myTime # Date time object .NOTES This component will only be used inside the module. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] [OutputType([DateTime])] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [int]$UnixTime ) # Provide the IP or DNS name to your Vera Controller. Write-Verbose "$($MyInvocation.MyCommand):: Will now try to convert `"$UnixTime`" Unix time to Datetime" $UnixStartTime = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0 [datetime]$Returntime = $UnixStartTime.AddSeconds($UnixTime) Write-Verbose "$($MyInvocation.MyCommand):: After translation this was the result $Returntime" return $Returntime } # End Get-MJUnixTimeToNormal function Set-MjVeramode { <# .Synopsis Sets current mode on Vera Controller with UI7. .DESCRIPTION This will allow you to change mode on your Vera controller. Created by Ispep Added 2016-06-06 WWW.Automatiserar.se .EXAMPLE .EXAMPLE .NOTES This component requires you to have a Vera controller. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] [OutputType([bool])] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$VeraIP, # Provide the IP or DNS name to your Vera Controller. [Parameter(Mandatory=$true, ValueFromPipeline=$true)][validateset('Home','Away','Night','Vacation')]$NewVeraMode, [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [System.Management.Automation.PSCredential]$VeraCredential ) # This url REQUIRES 1,2,3,4 in the last index of this string, 1 � 4 represents 1:Home, 2:Away, 3:Night, 4:Vacation $VeraChangeModeURL = "http://$($VeraIP):3480/data_request?id=lu_action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=SetHouseMode&Mode=" if (!(Test-Connection $VeraIP -Count 1)){Write-Warning "Unable to connect to IP / name: $VeraIP!, will not continue"; break} # Gather information about what mode Vera currently running in. if ($RequireLogin) { # Beginning to check what mode the controller is in, this function will use username and password. Write-Verbose "$($MyInvocation.InvocationName):: Requesting data with this username: $($VeraCredential.UserName)" $CurrentVeraMode = Get-MjVeraMode -VeraIP $VeraIP -RequireLogin -VeraCredential $VeraCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: Requesting data Requesting data without username" $CurrentVeraMode = Get-MjVeraMode -VeraIP $VeraIP } Write-Verbose "$($MyInvocation.InvocationName):: `$CurrentVeraMode.mode = $($CurrentVeraMode) `$NewVeraMode = $NewVeraMode" if ($CurrentVeraMode.mode -eq $NewVeraMode) { Write-Verbose "$($MyInvocation.InvocationName):: Your controller $veraIP is currently in the mode $CurrentVeraMode , Will not try to change mode to $($NewVeraMode)." Write-Warning "Already in the mode $newVeraMode, will not change mode" return $false } else { # This switch will convert the readable value to an int value that�s required by vera:s controller $VeraModeToSet = switch ($NewVeraMode) { 'Home' {1 ; break} 'Away' {2 ; break} 'Night' {3 ; break} 'Vacation' {4 ; break} Default {Write-Error "no value found that matches $NewVeraMode"; break} } if (!($?)){ Write-Error "Will not change mode due to wrong value provided ($NewVeraMode!" break } Write-Verbose "$($MyInvocation.InvocationName):: Will now try to change your veras mode to the the following value $($VeraModeTOSet) ($NewVeraMode)" $tmpVeraControllURL = "$($VeraChangeModeURL)" + "$VeraModeToSet" # will now try to change Veras Mode to the new value, this will check if the controller requires Login or not if ($RequireLogin) { Write-Verbose "$($MyInvocation.InvocationName):: Will now run with the username $($VeraCredential.UserName) on $VeraIP with http url: $tmpVeraControllURL" $TmpVeraResponse = Invoke-WebRequest -Uri $tmpVeraControllURL -Credential $VeraCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: Will now run http command on $VeraIP with http url: $tmpVeraControllURL" $TmpVeraResponse = Invoke-WebRequest -Uri $tmpVeraControllURL } if ($TmpVeraResponse.StatusCode -eq 200){ Write-Verbose "$($MyInvocation.InvocationName):: The http command was successful, Return code that was given was $(([xml]($TmpVeraResponse.Content)).SetHouseModeResponse.ok)" return $true } else{ # If code that�s not 200 is returned it will throw an error to inform that the command may not have succeeded. Write-Error "The http command did not get corret statuscode!, response code was $($TmpVeraResponse.StatusCode)" return $false } } } # end Set-MJVeraMode function Get-MJWebCamImage { <# .Synopsis Connects to webbcams and downloads an image. .DESCRIPTION Connects to webcams and downloads an image to local drive. Currently supports login and costume filename. Created by Ispep WWW.Automatiserar.se .EXAMPLE Get-MJWebCamImage -WebcamIP 10.20.30.40 -Destination C:\Temp -SnapshotURL "image/jpeg.cgi" WebcamIP : 10.20.30.40 Destination : C:\Temp SnapshotURL : image/jpeg.cgi Filename : CameraImage RequireLogin : True WebCamSource : http://10.20.30.40/image/jpeg.cgi SavePath : C:\Temp\CameraImage_2016-06-08_21-04-22.jpg FileSize : 47619 .EXAMPLE # Rhis will use login on cameras Get-MJWebCamImage -WebcamIP 10.20.30.40 -Destination C:\Temp -SnapshotURL "image/jpeg.cgi" -RequireLogin -CamCredential (Get-Credential) WebcamIP : 10.20.30.40 Destination : C:\Temp SnapshotURL : image/jpeg.cgi Filename : CameraImage RequireLogin : True WebCamSource : http://10.20.30.40/image/jpeg.cgi SavePath : C:\Temp\CameraImage_2016-06-08_21-04-22.jpg FileSize : 47619 .NOTES This component requires you to have a webcamera. #> [CmdletBinding()] Param ( # Provide an IP to the camera you would like to download an image from. [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, HelpMessage="Provide the IP to the Camera")] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$WebcamIP, [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, HelpMessage="Provide an path to save images")] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$Destination, [string]$SnapshotURL = "", # Provide a path to the url in the camera where the image is available [string]$Filename = "CameraImage", # This will be the first part of the name of the image. [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [System.Management.Automation.PSCredential]$CamCredential ) Write-Verbose "$($MyInvocation.InvocationName):: WebcamIP: $WebcamIP and Destination $Destination" # Verify Connection if (!(Test-Connection $WebcamIP -Count 1)){Write-Warning "Unable to connect to IP / name: $WebcamIP!, will not continue"; break} # Verify that username is present. if ($RequireLogin.IsPresent){ if (($CamCredential.UserName).Length -le 3){ Write-Error "No credential was provided!" break } else { Write-Verbose "$($MyInvocation.InvocationName):: Username $($CamCredential.UserName) was ok" } } # Buildning the filepath to use. $Tmp_FileName = [string] #$Filename + "_$(get-date -UFormat %Y-%m-%d_%H-%M-%S)" + ".jpg" # buildning path to save image to. $SavePath = [string] # This will verify that the image does not exist already. $tmp_Number = 0 $tmp_exist = $true while ($tmp_exist){ $Tmp_FileName = $Filename + "_$(get-date -UFormat %Y-%m-%d_%H-%M-%S)_$tmp_Number" + ".jpg" $SavePath= (Join-Path -ChildPath $Tmp_FileName -Path $Destination) if ((Test-Path $SavePath) -and $tmp_Number -le 10){ $tmp_Number++ } else { $tmp_exist = $false # exiting loop if the counter exeeds 10 } } # Creating webclient. $WC = New-Object System.Net.WebClient Write-Verbose "$($MyInvocation.InvocationName):: The filepath will be: $SavePath" if ($RequireLogin) { Write-Verbose "$($MyInvocation.InvocationName):: Adding $($CamCredential.UserName) and password to the webclient" $WC.Credentials = $CamCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: No username or password required" # no username required } # no snapshot url was provided, this will default to one of the default cameras thats available. if ($null -eq $SnapshotURL){ # add dlink, foscam, hikvsion support here!. } if ($SnapshotURL -match "^http"){ # $snapshoturl shuld be the total path to the camera due to the http in the beginning. } else { Write-Verbose "$($MyInvocation.InvocationName):: No Http was provided, will try to build the url. Old SnapshotURL = $SnapshotURL" $FullWebCamSource = "http://" + $((Join-Path -Path $WebcamIP -ChildPath $SnapshotURL).Replace('\','/')) Write-Verbose "$($MyInvocation.InvocationName):: Created destination URL: $FullWebCamSource" } # Verify if the destination exist if (!(Test-Path $Destination)) { Write-Warning "Folder $Destination does not exist, should the folder be created?" New-Item -Path $Destination -ItemType directory -Confirm | Out-Null if ($?){Write-Verbose "Folder $Destination was created OK"} else {Write-Error "Unable to create folder $Destination, no image was saved!"; break} } # Will build an object to return to the pipe. $ObjectToReturn = [ordered]@{ WebcamIP = $WebcamIP Destination = $Destination SnapshotURL = $SnapshotURL Filename = $Filename RequireLogin = if ($RequireLogin){$true}else{$false} WebCamSource = $FullWebCamSource SavePath = $SavePath FileSize = [int] # This will be added later. } $ReturnObject = New-Object -TypeName psobject -Property $ObjectToReturn try { $wc.DownloadFile($FullWebCamSource, $SavePath) ### if ($?) { # create a object and return all info about the image. $ReturnObject.FileSize = (Get-ItemProperty $SavePath).Length $ReturnObject } } catch { Write-Warning "Unable to download image: $SavePath fr�n $FullWebCamSource" #Write-Warning $($Error[0]).Exception if ($Error[0].Exception -match "(401)"){Write-Error "Your camera requires login credentials, Verify username and password if credential was provided. No image where saved"; break} } } # End Get-MJWebCamImage function Save-MJVeraWebCamImage { <# .Synopsis Connects to Vera controller and collects information about all cameras and saves images. .DESCRIPTION This function will call Get-MJWebCamImage and provide information provided by Get-MJVeraStatus, Images from all webcams will be saved to disk. This will provide new objects with information about all images that�s saved with this function. 2016-06-21 - Added the room the device was found in. Currently supports up 10 images / sec (limit with the filename currently) Created by Ispep Added 2016-06-07 WWW.Automatiserar.se .EXAMPLE Save-MJVeraWebCamImage -VeraIP vera -DestinationPath C:\Temp -Filename VeraImage WebcamIP : 10.20.30.40 Destination : C:\Temp SnapshotURL : image/jpeg.cgi Filename : VeraImage RequireLogin : True WebCamSource : http://10.20.30.40/image/jpeg.cgi SavePath : C:\Temp\VeraImage_2016-06-08_23-06-03_3.jpg FileSize : 48454 Room : 1 .EXAMPLE Save-MJVeraWebCamImage -VeraIP vera -DestinationPath C:\Temp -Filename VeraImage -RequireLogin -VeraCredential (Get-Credential) WebcamIP : 10.20.30.40 Destination : C:\Temp SnapshotURL : image/jpeg.cgi Filename : VeraImage RequireLogin : True WebCamSource : http://10.20.30.40/image/jpeg.cgi SavePath : C:\Temp\VeraImage_2016-06-08_23-07-51_0.jpg FileSize : 48498 Room : 1 .NOTES This component requires you to have a Vera controller. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$VeraIP, # Provide the IP or DNS name to your Vera Controller. [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$DestinationPath, # provide the File path where you want files to be saved to [string]$Filename = "VeraImage", [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [System.Management.Automation.PSCredential]$VeraCredential ) Write-Verbose "$($MyInvocation.InvocationName):: Beginning to collect information form Vera." $VeraStatusObject = @() if ($RequireLogin) { Write-Verbose "$($MyInvocation.InvocationName):: Login will be used" if ($RequireLogin.IsPresent){ if (($VeraCredential.UserName).Length -le 3){ Write-Error "No credential was provided!" break } else { Write-Verbose "$($MyInvocation.InvocationName):: Username $($VeraCredential.UserName) was ok" } } $VeraStatusObject = Get-MJVeraStatus -VeraIP $VeraIP -RequireLogin -VeraCredential $VeraCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: No login will be used on $VeraIP" $VeraStatusObject = Get-MJVeraStatus -VeraIP $VeraIP } Write-Verbose "$($MyInvocation.InvocationName):: Sending information into sorting function and extracting all cameras." $VeraStatusObject.DeviceVerbose | Where-Object {$_.device_file -match "D_DigitalSecurityCamera"} | ForEach-Object { $tmpObj = $_ if ($tmpObj.username){ Write-Verbose "$($MyInvocation.InvocationName):: Username was found on camera $($tmpObj.ip)" # DUE TO THE SECURE STRING REQUIREMENT THIS WAS ADDED.... The password IS plaintext in VERA, so there�s no workaround except this.... // Ispep... $SecureString = New-Object -TypeName System.Security.SecureString foreach ($tmpPass in (($tmpObj.password).ToCharArray())){$SecureString.AppendChar($tmpPass)} $TMP_ResultObj = Get-MJWebCamImage -WebcamIP $($tmpObj.ip) -Destination $DestinationPath -Filename $Filename -SnapshotURL $(($tmpObj.states | Where-Object {$_.variable -eq "URL"}).value) -RequireLogin -CamCredential (New-Object System.Management.Automation.PSCredential(($tmpObj.username),($SecureString))) $TMP_ResultObj | Add-Member -MemberType NoteProperty -Name "Room" -Value ($tmpObj.room) return $TMP_ResultObj } else { Write-Verbose "$($MyInvocation.InvocationName):: No username or password will be applied to the camera" $TMP_ResultObj = Get-MJWebCamImage -WebcamIP $($tmpObj.ip) -Destination $DestinationPath -Filename $Filename -SnapshotURL $(($tmpObj.states | Where-Object {$_.variable -eq "URL"}).value) $TMP_ResultObj | Add-Member -MemberType NoteProperty -Name "Room" -Value ($tmpObj.room) return $TMP_ResultObj } } } # End Save-MJVeraWebCamImage function Set-MJVeraDevice { <# .Synopsis Turn or off devices within vera with a singe command .DESCRIPTION This function will set devices mode in Vera, this function will be limited to a few device types until I tested each class enough Created by Ispep Added 2016-06-09 WWW.Automatiserar.se .EXAMPLE # This command will connect to the controller named �Vera� and turn off device 80 Set-MJVeraDevice -VeraIP vera -DeviceID 80 -Action OFF DeviceID : 80 DeviceName : My Device Name DeviceExist : True DeviceChangedMode : True DevicePowerDevice : True deviceType : urn:schemas-upnp-org:device:BinaryLight:1 CommandSent : True .EXAMPLE # If the device is in the current mode no command is sent to the controller. Set-MJVeraDevice -VeraIP vera -DeviceID 80 -Action OFF WARNING: Device 80 / (Lampa Window) - Currently in the same mode as you are trying to set: (OFF). DeviceID : 80 DeviceName : My Device Name DeviceExist : True DeviceChangedMode : False DevicePowerDevice : True deviceType : urn:schemas-upnp-org:device:BinaryLight:1 CommandSent : False .EXAMPLE Set-MJVeraDevice -VeraIP vera -DeviceID 10,11,12,33,50 -Action OFF .NOTES This requires a Vera Controller with UI7 to work #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$VeraIP, # Provide the IP or DNS name to your Vera Controller. [parameter(Mandatory=$true)][string[]]$DeviceID, [parameter(Mandatory=$true)][ValidateSet('ON','OFF',ignorecase=$true)][string]$Action, [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [System.Management.Automation.PSCredential]$VeraCredential ) if (!(Test-Connection $VeraIP -Count 1)){Write-Warning "Unable to connect to IP / name: $VeraIP!, will not continue"; break} # Gather information about what mode Vera currently running in. $All_Results = @() # This variable will contain all devices that where searched in the function. $tmp_Object = @() # This will be used later in the function $PowerSwitchScheme = "urn:upnp-org:serviceId:SwitchPower1" # Scheme for switches. $PowerDevice_Type = "urn:schemas-upnp-org:device:BinaryLight:1" # This will narrow the search down to only the correct devices. [string]$tmp_Action = if ($Action -eq "ON"){"1"} elseif ($Action -eq "OFF"){"0"} else {Write-Error "This command does not support this command!";break} Write-Verbose "$($MyInvocation.InvocationName):: Begin to verify devices $DeviceID " if ($RequireLogin) { # Beginning to check what mode the controller is in, this function will use username and password. Write-Verbose "$($MyInvocation.InvocationName):: Requesting data with this username: $($VeraCredential.UserName)" $tmp_Object = Get-MJVeraStatus -VeraIP $VeraIP -RequireLogin -VeraCredential $VeraCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: Requesting data Requesting data without username" $tmp_Object = Get-MJVeraStatus -VeraIP $VeraIP } # This will be the object to check Write-Verbose "$($MyInvocation.InvocationName):: Will now check This devices: $deviceid" foreach ($TMP_DeviceID in $DeviceID){ # Build the controll string for each device. $VeraPowersSwitchURL = "http://$($VeraIP):3480/data_request?id=lu_action&output_format=xml&DeviceNum=$($TMP_DeviceID)&serviceId=$($PowerSwitchScheme)&action=SetTarget&newTargetValue=" # Crate a object that will get all information added and returned to pipeline, default False $TMP_DeviceObject = [ordered]@{ DeviceID = [string]$TMP_DeviceID # DevicesID DeviceName = [string]"" # Name of the device if it exits. DeviceExist = [bool]$false # true if exists DeviceChangedMode = [bool]$false # true if the function changes mode on device. DevicePowerDevice = [bool]$false # true if Switch DeviceType = [string]"" # this will be the scheme used by the device. CommandSent = [bool]$false # this will be set to True if a device was controlled with the command } $ObjectToReturn = New-Object -TypeName psobject -Property $TMP_DeviceObject $Tmp_Result = $tmp_Object.DeviceVerbose | Where-Object {$_.id -eq $TMP_DeviceID -and $_.states.service -eq $PowerSwitchScheme -and $_.states.variable -eq "Status" -and $_.device_type -eq "$PowerDevice_Type"} if ($Tmp_Result){ $Tmp_ValueCheck = @() # Validating if the device exist $Tmp_ValueCheck = $Tmp_Result.states | Where-Object {$_.service -eq $PowerSwitchScheme -and $_.variable -eq "Status"} if ($Tmp_ValueCheck){ Write-Verbose "$($MyInvocation.InvocationName):: Device are ready to be controlled!" # Are the current value the same as the one the controller currently in? if ($tmp_Action -eq $Tmp_ValueCheck.value){ Write-Warning "Device $TMP_DeviceID / ($($Tmp_Result.name)) - Currently in the same mode as you are trying to set: ($Action)." $ObjectToReturn.DeviceExist = $true $ObjectToReturn.DevicePowerDevice = $true $ObjectToReturn.DeviceName = $($Tmp_Result.name) $ObjectToReturn.devicetype = $($Tmp_Result.device_type) $All_Results += $ObjectToReturn # This will return an false result } else { Write-Verbose "$($MyInvocation.InvocationName):: Device $TMP_DeviceID / ($($Tmp_Result.name)) currently is not in the same mode as you are trying to set" Write-Verbose "$($MyInvocation.InvocationName):: Change mode on $TMP_DeviceID ($($Tmp_Result.name)) to $Action" Write-Verbose "$($MyInvocation.InvocationName):: Sending url: $($VeraPowersSwitchURL)$tmp_Action" $TmpHTTP_Result = Invoke-RestMethod -Uri "$($VeraPowersSwitchURL)$tmp_Action" -Method Get Write-Verbose "$($MyInvocation.InvocationName):: Result was $TmpHTTP_Result" $ObjectToReturn.DeviceExist = $true $ObjectToReturn.DevicePowerDevice = $true $ObjectToReturn.DeviceName = $($Tmp_Result.name) $ObjectToReturn.DeviceChangedMode = $true $ObjectToReturn.devicetype = $($Tmp_Result.device_type) $ObjectToReturn.CommandSent = $true $All_Results += $ObjectToReturn # This will return an true result } } else { Write-Warning "No Powershwich function was found on device $TMP_DeviceID, name: $($Tmp_Result.name) " $ObjectToReturn.DeviceExist = $true $ObjectToReturn.DeviceName = $($Tmp_Result.name) $ObjectToReturn.devicetype = $($Tmp_Result.device_type) $All_Results += $ObjectToReturn # This will return an false result } } else { # scheme did not match, will check if device exists. $Tmp_MatchOnDeviceID = $tmp_Object.DeviceVerbose | Where-Object {$_.id -eq $TMP_DeviceID} if ($Tmp_MatchOnDeviceID){ Write-Warning "No Powershwich function was found on device $($Tmp_MatchOnDeviceID.name) with ID $($Tmp_MatchOnDeviceID.id)" $ObjectToReturn.DeviceExist = $true $ObjectToReturn.DeviceName = $($Tmp_MatchOnDeviceID.name) $ObjectToReturn.devicetype = $Tmp_MatchOnDeviceID.device_type $All_Results += $ObjectToReturn } else { Write-Warning "No device with ID $TMP_DeviceID was found!" $All_Results += $ObjectToReturn } # This will return an false result } } return $All_Results # returns all info } # END Set-MJVeraDevice function Set-MJImageInformation { <# .Synopsis This will open an image and add information to the image. .DESCRIPTION This function will allow you to paint information into images and then save them for use. it�s a part of the module I use for the home automation project with Vera Created by Ispep Added 2016-06-21 WWW.Automatiserar.se .EXAMPLE # This will load the image MyImage.jpg and save a new image in path: C:\temp\MyResult.jpg with the date information and 22 Celcius added to the image Set-MJImageInformation -SourceImage C:\Temp\MyImage.jpg -DestinatinPath C:\temp\MyResult.jpg -Information "$(get-date)","22 Celsius" .EXAMPLE # this will load the image MyImage.jpg and save a new image in path: C:\temp\MyResult.jpg with date and "Motion Detected" information. this info will be saved in the images X 100 and Y 200 with a font size of 12 Set-MJImageInformation -SourceImage C:\Temp\MyImage.jpg -DestinatinPath C:\temp\MyResult.jpg -Information "$(get-date)","Motion Detected" -ImageInfoLocationWidth 100 -ImageInfoLocationHeight 200 -FontSize 12 .NOTES This component requires an image to exist before it�s possible to write in the image. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$SourceImage, # Provide a path to the image you want to uppdate with information. [string]$DestinatinPath, # Provide the path where the image should be saved. [string[]]$Information, # Add the strings you want to add to the image. [int]$ImageInfoLocationWidth = 15, # Provide the Image X location where you want the information to be written. [int]$ImageInfoLocationHeight = 10, # Provide the image Y location [int]$FontSize = 12, # This allows you to set the font size [int]$RectangleTop = 80, # width of the rectangle [int]$RectangleRight = 80, # height of the rectangle [int]$Rectangletransparency = 100 # provide the transparency you want to have on the rectangle ) begin { #Load .net assemblys [Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null if (!(Test-Path $SourceImage)){Write-Error "Unable to find the file $SourceImage"; break} # Load the image file $srcImg = [System.Drawing.Image]::FromFile($SourceImage) # Create an bitmap object $bmpFile = new-object System.Drawing.Bitmap([int]($srcImg.width)),([int]($srcImg.height)) $Image = [System.Drawing.Graphics]::FromImage($bmpFile) $Image.SmoothingMode = "AntiAlias" # write the image information into the rectangle $Rectangle = New-Object Drawing.Rectangle 0, 0, $srcImg.Width, $srcImg.Height $Image.DrawImage($srcImg, $Rectangle, 0, 0, $srcImg.Width, $srcImg.Height, ([Drawing.GraphicsUnit]::Pixel)) } # end - Begin process { # This section will allow you to change where the information is written. $TopWidth = $(if ($ImageInfoLocationWidth -gt $srcImg.Width){Write-Error "This value is greater than the image with!"; break} else {$ImageInfoLocationWidth}) # sets the image location with $TopHeight = $(if ($ImageInfoLocationWidth -gt $srcImg.Width){Write-Error "This value is greater than the image height!"; break} else {$ImageInfoLocationHeight}) # sets the image location height $StartLocation = $TopHeight # This variable is used to get the start location for all information. # This section will try to resolve how big the rectangle should be. $RectangleTextWidth = 0 $RectangleHeight = (($Information.Count) * ($FontSize + 7)) -as [int] # This will check all text and add make the rectangle the needed width foreach ($tmpinfo in $Information){ if ($RectangleTextWidth -le (($($tmpinfo.Length) * 10) * ($FontSize/12))){ $RectangleTextWidth = (($($tmpinfo.Length) * 10) * ($FontSize/12)) -as [int] } Write-Verbose "$($MyInvocation.InvocationName)::[Foreach] The text length is $($tmpinfo.length), the variable is $RectangleTextWidth" Write-Verbose "$($MyInvocation.InvocationName)::[Foreach] Verify if the text will fit into the image, text: $($RectangleTextWidth) vs $($srcImg.width)" # If the information risks to be written outside the image there will be a warning, but the image will still be written. if (($ImageInfoLocationWidth + $RectangleTextWidth) -ge ($srcImg.Width)){ Write-Warning "Some text will be written outside this image!" Write-Verbose "$($MyInvocation.InvocationName)::[if-True] the text will end in this index: $($ImageInfoLocationWidth + $RectangleTextWidth), the image is only $($srcImg.Width)" } } # end foreach Write-Verbose "$($MyInvocation.InvocationName):: The information will be written at indexes $ImageInfoLocationWidth, $ImageInfoLocationHeight, $RectangleTextWidth, $RectangleHeight" $Brush = New-Object Drawing.SolidBrush ([System.Drawing.Color]::FromArgb($rectangletransparency,0,0,0)) $Image.FillRectangle($Brush, $ImageInfoLocationWidth, $ImageInfoLocationHeight, $RectangleTextWidth, $RectangleHeight) # This part will add information to the image. ForEach ($TMPInfo in $Information){ $Font = new-object System.Drawing.Font("Verdana", $FontSize) $Brush = New-Object Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(255, 255, 255,255)) # This will set the text color White $Image.DrawString("$TMPInfo", $Font, $Brush, $TopWidth , $StartLocation) $StartLocation += ($FontSize + 5) } $srcImg.Dispose() $bmpFile.save($DestinatinPath, [System.Drawing.Imaging.ImageFormat]::jpeg) $bmpFile.Dispose() } # Process end { return $true } # end } # END Set-MJImageInformation function Save-MJVeraImageWithInfo { <# .Synopsis Saves all images from vera with all sensors in that room. .DESCRIPTION This will allow you to add more information to your vera device. Created by Ispep Added 2016-06-21 WWW.Automatiserar.se .EXAMPLE # This will download and add all sensor information to the image in Vera. Save-MJVeraImageWithInfo -VeraIP VeraIP -Destinationpath C:\temp WebcamIP : 10.20.30.40 Destination : C:\temp SnapshotURL : image/jpeg.cgi Filename : VeraImage RequireLogin : True WebCamSource : http://10.20.30.40/image/jpeg.cgi SavePath : C:\temp\VeraImage_2016-06-21_21-30-27_3.jpg FileSize : 49211 Room : 13 .EXAMPLE # This will download and add all sensor information to the image in Vera. Save-MJVeraImageWithInfo -VeraIP VeraIP -Destinationpath C:\temp -Infotransparency 255 WebcamIP : 10.20.30.40 Destination : C:\temp SnapshotURL : image/jpeg.cgi Filename : VeraImage RequireLogin : True WebCamSource : http://10.20.30.40/image/jpeg.cgi SavePath : C:\temp\VeraImage_2016-06-21_21-30-27_3.jpg FileSize : 49211 Room : 13 .NOTES This component requires you to have a Vera controller. #> [CmdletBinding(PositionalBinding=$false, HelpUri = 'http://www.automatiserar.se/powershell-modul-for-hemautomation/', ConfirmImpact='Medium')] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [ValidateNotNullOrEmpty()] [string]$VeraIP, # Provide the IP or DNS name to your Vera Controller. [switch]$RequireLogin, # If you require your controller to apply login use this switch and provide credential to your vera.. [string]$Destinationpath, # Provide the path where you want the images to be saved. [int]$FontSize = 10, # Provude the fontsize [int]$Infotransparency = 125, # Sets the transperency to 50% min 0 max 255 for the black [System.Management.Automation.PSCredential]$VeraCredential ) begin { if (!(Test-Connection $VeraIP -Count 1)){Write-Warning "Unable to connect to IP / name: $VeraIP!, will not continue"; break} # verify Destination folder and creates it if needed. if (!(Test-Path $Destinationpath)){ Write-Warning "Unable to find folder $Destinationpath, will create it now." New-Item -Path $Destinationpath -ItemType directory | Out-Null if (!($?)){ Write-Error "Unable to create a folder in path $Destinationpath" break } } $AllImages = @() # Create an empty object $AllResults = @() # This will provide info about all images. $AllVeraDevices = @() # Gather information about what mode Vera currently running in. if ($RequireLogin) { # Beginning to check what mode the controller is in, this function will use username and password. Write-Verbose "$($MyInvocation.InvocationName):: Requesting data with this username: $($VeraCredential.UserName)" $AllImages = Save-MJVeraWebCamImage -VeraIP $VeraIP -RequireLogin -VeraCredential $VeraCredential $AllVeraDevices = Get-MJVeraStatus -VeraIP $VeraIP -RequireLogin -VeraCredential $VeraCredential } else { Write-Verbose "$($MyInvocation.InvocationName):: Requesting data Requesting data without username" $AllImages = Save-MJVeraWebCamImage -VeraIP $VeraIP -DestinationPath $Destinationpath $AllVeraDevices = Get-MJVeraStatus -VeraIP $VeraIP } } process { foreach ($Imgfile in $allImages){ $InfoToadd = @() # This is the information to add to the image. $DevicesInRoom = $AllVeraDevices.DeviceVerbose | Where-Object {$_.room -eq $($Imgfile.Room)} foreach ($DeviceInRoom in $DevicesInRoom){ switch ($DeviceInRoom.device_type){ 'urn:schemas-micasaverde-com:device:HumiditySensor:1' {$InfoToadd += "$($DeviceInRoom.name): $($DeviceInRoom.states | Where-Object {$_ -match "urn:micasaverde-com:serviceId:HumiditySensor1"} | Select-Object -ExpandProperty value)"} 'urn:schemas-micasaverde-com:device:LightSensor:1' {$InfoToadd += "$($DeviceInRoom.name): $($DeviceInRoom.states | Where-Object {$_ -match "urn:micasaverde-com:serviceId:LightSensor1"} | Select-Object -ExpandProperty value)"} 'urn:schemas-micasaverde-com:device:TemperatureSensor:1' {$InfoToadd += "$($DeviceInRoom.name): $($DeviceInRoom.states | Where-Object {$_ -match "urn:upnp-org:serviceId:TemperatureSensor1" -and $_ -match "CurrentTemperature"}| Select-Object -ExpandProperty value)"} 'urn:schemas-micasaverde-com:device:DoorSensor:1' {$InfoToadd += "$($DeviceInRoom.name): $(if ($($DeviceInRoom.states | Where-Object {$_ -match "urn:micasaverde-com:serviceId:SecuritySensor1" -and $_ -match "Tripped"} | Select-Object -ExpandProperty value) -eq 0){"Closed"} else {"OPEN"})"} 'urn:schemas-micasaverde-com:device:MotionSensor:1' {$InfoToadd += "$($DeviceInRoom.name): $(if ($($DeviceInRoom.states | Where-Object {$_ -match "urn:micasaverde-com:serviceId:SecuritySensor1" -and $_ -match "Tripped"} | Select-Object -ExpandProperty value) -eq 0){"NO"} else {"YES"})"} Default {Write-Verbose "$($MyInvocation.InvocationName):: This device was not configured in this function $($deviceInRoom.device_type)"} } } Set-MJImageInformation -SourceImage "$($Imgfile.SavePath)" -DestinatinPath "$($Imgfile.SavePath)" -Information $InfoToadd -Rectangletransparency $Infotransparency $AllResults += $Imgfile } } end { return $AllResults } } # END Save-MJVeraImageWithInfo |