Get-VEMeasurements.ps1
|
function Get-VEMeasurements { <# .SYNOPSIS Return measurement data from a site .DESCRIPTION Return measurement data from a site .INPUTS The VE site object can be piped to the function .EXAMPLE $VESite | Get-VEMeasurements Example of how to get measurement data from a site using the VRM API .NOTES Author: Decembry Quentin #> [CmdletBinding(DefaultParameterSetName='TimeSpan')] [OutputType([PSCustomObject])] [Alias()] param( # The VE session # Default: the default VE session [Parameter(Mandatory=$false)] [PSTypeName('VictronEnergy.Session')] $Session = $Script:VE_Session, # The VE device [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [PSTypeName('VictronEnergy.Device')] $Device, # The VE measurement attibutes # Default: $Device | Get-VEMeasurementAttributes [Parameter(Mandatory=$false)] [PSTypeName('VictronEnergy.MeasurementAttribute')] $MeasurementAttributes, # Time span [Parameter(Mandatory=$false,ParameterSetName='TimeSpan')] [VictronEnergy.TimeSpan] $TimeSpan = [VictronEnergy.TimeSpan]::Last24Hours, # Start time # Default: (Get-Date -Millisecond 0 -Second 0).AddDays(-1) -> Yesterday [Parameter(Mandatory=$false,ParameterSetName='DateTime')] [DateTime] $StartTime = (Get-Date -Millisecond 0 -Second 0).AddDays(-1), # End time # Default: (Get-Date -Millisecond 0 -Second 0) -> Now [Parameter(Mandatory=$false,ParameterSetName='DateTime')] [DateTime] $EndTime = (Get-Date -Millisecond 0 -Second 0) ) begin { if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } if ($MyInvocation.MyCommand.ModuleName) { $Private:ThisFunction = '{0}::{1}' -f $MyInvocation.MyCommand.ModuleName, $MyInvocation.MyCommand.Name } else { $Private:ThisFunction = '{0}::{1}' -f (Get-PSCallStack)[0].ScriptName, $MyInvocation.MyCommand.Name } Write-Debug "[$Private:ThisFunction] Begin" Write-Debug "[$Private:ThisFunction] Session token validation" if (!$Session.Token) { throw 'Unable to list devices without a session token' } $TSTimestamps = switch ($PSCmdlet.ParameterSetName) { 'TimeSpan' { $TimeSpan | Get-VETimeSpanTimestamps } 'DateTime' { Get-VETimeSpanTimestamps -StartTime $StartTime -EndTime $EndTime } default { throw "Unsupported parameter set name '$_'" } } } process { Write-Debug "[$Private:ThisFunction] Process device $Device" [string] $SiteId = $Device.IdSite Write-Debug "[$Private:ThisFunction] Measurement attributes validation" if (!$MeasurementAttributes) { $VEMeasurementAttributes = Get-VEMeasurementAttributes -Device $Device -ErrorAction Stop } if (!$VEMeasurementAttributes) { Write-Error "Invalid measurement attributes for device $Device" break; } $AttributeCodes = ($VEMeasurementAttributes | ForEach-Object { 'attributeCodes[]={0}' -f $_.Code }) -join '&' $Uri = $Script:VE_VrmApiBaseUri + ('installations/{0}/widgets/Graph?{1}&instance={2}&start={3}&end={4}' -f $Device.IdSite, $AttributeCodes, $Device.instance, $TSTimestamps.Start, $TSTimestamps.End) $Headers = @{ 'X-Authorization' = 'Bearer {0}' -f $Session.Token } try { Write-Debug "[$Private:ThisFunction] Send request to '$Uri'" $WebRequest = Invoke-WebRequest -UseBasicParsing -Uri $Uri -Method GET -Headers $Headers -ErrorAction Stop $Json = ConvertFrom-Json -InputObject $WebRequest.Content -ErrorAction Stop Write-Debug "[$Private:ThisFunction] Response validation" if ($Json.success -isnot [Boolean]) { throw "Invalid content received from server" } if (!$Json.success) { throw "Error occured" } Write-Debug "[$Private:ThisFunction] Processing records" foreach ($Record in $Json.Records) { Write-Debug "[$Private:ThisFunction] Process data from record (1/2)" $VECodes = @() $RawData = foreach ($Key in $Record.Data.psobject.Properties.Name) { Write-Debug "[$Private:ThisFunction] Processing key '$Key'" $Meta = $Record.Meta.$Key if (!$Record.Data.$Key) { Write-Debug "[$Private:ThisFunction] No data available for attribute '$($Meta.Description)' (code $($Meta.code))" continue } if (!($Formatter = $Script:VE_ValueFormatter.$SiteId.([string]$Meta.Code))) { Write-Debug "[$Private:ThisFunction] Create value formatting object from meta object" $Formatter = Set-VEValueFormatter -Meta $Meta -SiteId $SiteId -PassThru -ErrorAction Stop } $Record.Data.$Key | ForEach-Object { [PSCustomObject] @{ timestamp = $_[0] value = $Formatter.Format($_[1]) code = $Meta.code } } $Hashtable = $Meta | ConvertTo-Hashtable $Hashtable.PSTypeName = 'VictronEnergy.MeasurementMeta' $Hashtable.Significant = $true $Hashtable.Minimum = $null $Hashtable.Average = $null $Hashtable.Maximum = $null $Object = [PSCustomObject] $Hashtable [string[]] $PropertiesToDisplay = @( 'Code', 'Description', 'Minimum', 'Average', 'Maximum' ) $PSPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$PropertiesToDisplay) $PSMemberInfo = [System.Management.Automation.PSMemberInfo[]]@($PSPropertySet) $Object | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSMemberInfo -Force $VECodes += $Object } Write-Debug "[$Private:ThisFunction] Process data from record (2/2)" $VEData = $RawData | Group-Object timestamp | ForEach-Object { # Create hashtable with timestamp and datetime $Hashtable = @{ PSTypeName = 'VictronEnergy.MeasurementData' timestamp = $_.Name datetime = '{0:yyyy-MM-dd HH:mm:ss}' -f [System.DateTimeOffset]::FromUnixTimeSeconds($_.Name).LocalDateTime } # Add value for each code $_.Group | ForEach-Object { $Code = $Code0 = [string]$_.code while ($Hashtable.$Code) { # Object properties are case insensitive. Prevent overwriting a code with another only different by the case $Code += '.' } if ($Code -cne $Code0) { # Update codes list $VECodes | Where-Object { $_.Code -ceq $Code0 } | ForEach-Object { $_.Code = $Code } } $Hashtable.$Code = $_.Value } # Create object from hashtable $Object = [PSCustomObject] $Hashtable # Hide property 'timestamp' [string[]] $PropertiesToDisplay = @( 'datetime' ) + $VECodes.Code # $PropertiesToDisplay += $VECodes.Code $PSPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$PropertiesToDisplay) $PSMemberInfo = [System.Management.Automation.PSMemberInfo[]]@($PSPropertySet) $Object | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSMemberInfo -Force # Return object $Object } Write-Debug "[$Private:ThisFunction] Find most significant properties" $VECodes | ForEach-Object { $_.Significant = ($VEData.($_.Code).Value | Group-Object | Measure-Object).Count -gt 1 if ($VEData.($_.Code).NumValue.Count) { $Code = $_.Code -replace '\.+$', '' if ($Formatter = $Script:VE_ValueFormatter.$SiteId.$Code) { $Measure = $VEData.($_.Code).NumValue | Measure-Object -Average -Maximum -Minimum $_.Minimum, $_.Average, $_.Maximum = $Measure.Minimum, $Measure.Average, $Measure.Maximum | ForEach-Object { $Formatter.Format($_) } } } } $CodeSignificantProperties = $VECodes | Where-Object { $_.Significant } | Select-Object -ExpandProperty Code $DataSignificantProperties = @( 'datetime' ) + $CodeSignificantProperties $DataFormatTableProperties = @( 'datetime' ) + $VECodes.Code | ForEach-Object { $Width = ($VEData.$_ | ForEach-Object { if ($_) { $_.ToString().Length } else { 0 } } | Select-Object -Uniq | Sort-Object -Descending)[0] if ($_.Length -gt $Width) { $Width = $_.Length } if ($_ -ne 'datetime') { $Width++ } @{ name = "$_" expression = [ScriptBlock]::Create("`$_.'$_'") align = 'right' width = $Width } } $DataFormatTableSignificantProperties = $DataFormatTableProperties | Where-Object { $_.Name -in $DataSignificantProperties } Write-Debug "[$Private:ThisFunction] Create measurement object from record" $VEObject = [PSCustomObject] @{ PSTypeName = 'VictronEnergy.Measurements' Device = $Device Data = $VEData Codes = $VECodes CodeSignificantProperties = $CodeSignificantProperties DataSignificantProperties = $DataSignificantProperties DataFormatTableProperties = $DataFormatTableProperties DataFormatTableSignificantProperties = $DataFormatTableSignificantProperties } Write-Debug "[$Private:ThisFunction] Set properties to display" [string[]] $PropertiesToDisplay = @('Device', 'Data', 'Codes') $PSPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$PropertiesToDisplay) $PSMemberInfo = [System.Management.Automation.PSMemberInfo[]]@($PSPropertySet) $VEObject | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSMemberInfo -Force Write-Debug "[$Private:ThisFunction] Add script method to display data" $VEObject | Add-Member -MemberType ScriptMethod -Name WriteHost -Value { param( [Bool] $MostSignificant = $true, [Bool] $Details = $true ) $Codes, $DataProperties = if ($MostSignificant) { $this.CodeSignificantProperties, $this.DataFormatTableSignificantProperties } else { $this.Codes.Code, $this.DataFormatTableProperties } $this.Device | Format-Table | Out-String | Write-Host -NoNewLine $this.Codes | Where-Object { $_.code -in $Codes } | Out-String | Write-Host -NoNewLine if ($Details) { $this.Data | Format-Table -Property $DataProperties | Out-String | Write-Host -NoNewLine } } Write-Debug "[$Private:ThisFunction] Return measurement object" Write-Output -NoEnumerate -InputObject $VEObject } } catch { $_ | ForEach-Object { Write-Error $_ } } } end { Write-Debug "[$Private:ThisFunction] End" } } # SIG # Begin signature block # MIIIbQYJKoZIhvcNAQcCoIIIXjCCCFoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUMosNLV0aESUggEdCW51VRq0+ # bfGgggX4MIICYjCCAgigAwIBAgITXwAAAAITcsrrTJecaQAAAAAAAjAKBggqhkjO # PQQDAjAkMQswCQYDVQQGEwJCRTEVMBMGA1UEAxMMSG9tZSBSb290IENBMB4XDTIy # MDUwNTE0MDMxOFoXDTMyMDUwNTE0MTMxOFowJzELMAkGA1UEBhMCQkUxGDAWBgNV # BAMTD0hvbWUgSXNzdWluZyBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDOT # aIIU+QU7LfsmyPqGkCf53SrJuaBPpb5iVILCvG3cQxL6vgIZaGRaKlzYO+lq3CnN # b4rtJW2d7rlTeehXtpWjggEUMIIBEDAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4E # FgQUYf1/nMEp3Df+Nzi+Hvk7FymPsrMwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBD # AEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUfSS6 # P6qin/3ibmvelGJQhV1r1VgwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL3BraS5x # ZWRpbnV4LmJlL0hvbWUlMjBSb290JTIwQ0EuY3JsMEYGCCsGAQUFBwEBBDowODA2 # BggrBgEFBQcwAoYqaHR0cDovL3BraS5xZWRpbnV4LmJlL0hvbWUlMjBSb290JTIw # Q0EuY3J0MAoGCCqGSM49BAMCA0gAMEUCIERRzfMu3vx9vb+Apud4sDkOEKhKrCEl # gSsedbjZRJ+YAiEA8BBaOqhnbnsUBIX2nty77euunwoAHrCuWzab5xJiy0UwggOO # MIIDM6ADAgECAhNRAAAAMJpCm89a8zgYAAAAAAAwMAoGCCqGSM49BAMCMCcxCzAJ # BgNVBAYTAkJFMRgwFgYDVQQDEw9Ib21lIElzc3VpbmcgQ0EwHhcNMjIwNzExMDc0 # NzQ3WhcNMjQwNzExMDc1NzQ3WjAbMRkwFwYDVQQDExBEZWNlbWJyeSBRdWVudGlu # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjALWtmJ1AQG77y9rUBbT # HXR9FZokTP01bBWfNzk4Kenr/Xpm7Jt09PqLoNS8ToWWKRz9EkM+jg7lnLljWKcO # G+fT1TbaEwxOpncugnbrioQQonbdWpyn0P7PxlqjUQdWd5Z7xQ0Z8RO3vvERWtDA # L9QH0VBHNK4WqdWh3ElV6ogJUsy5hYxVTIU0K5VEYkIT4qvqq0RdajKavGWIr57R # EsYv52Jcyh9FNgZKGdu9NwSKZUvkwHxGsI8zmwfP1I6QRkzsqPXlpk7A8NTD3+h+ # 4Y5AprRFyAWrVNYH+UQQe2D/PjjbKtfsU9xzG9EnqJF09pVRchapis+8DbfWnoC8 # iQIDAQABo4IBfTCCAXkwOgYJKwYBBAGCNxUHBC0wKwYjKwYBBAGCNxUIhorlOcPr # DIadiwndkzuHjPAAPqb1CYWVxXgCAWQCAQowEwYDVR0lBAwwCgYIKwYBBQUHAwMw # DgYDVR0PAQH/BAQDAgeAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYBBQUHAwMwHQYD # VR0OBBYEFAkUh200U2U2epAoy2ZvT7nwAYGBMB8GA1UdIwQYMBaAFGH9f5zBKdw3 # /jc4vh75Oxcpj7KzMD4GA1UdHwQ3MDUwM6AxoC+GLWh0dHA6Ly9wa2kucWVkaW51 # eC5iZS9Ib21lJTIwSXNzdWluZyUyMENBLmNybDBJBggrBgEFBQcBAQQ9MDswOQYI # KwYBBQUHMAKGLWh0dHA6Ly9wa2kucWVkaW51eC5iZS9Ib21lJTIwSXNzdWluZyUy # MENBLmNydDAuBgNVHREEJzAloCMGCisGAQQBgjcUAgOgFQwTRGVjZW1icnkuUUBo # b21lLmxhbjAKBggqhkjOPQQDAgNJADBGAiEAh39vHCgqIDL4XazyHPiChMbu0by+ # aFP7rdzlJmM0K6ACIQCaP4CXolxlzdfkJsoIoDIJUrunYIlZ0yxs9OSnG1qmCjGC # Ad8wggHbAgEBMD4wJzELMAkGA1UEBhMCQkUxGDAWBgNVBAMTD0hvbWUgSXNzdWlu # ZyBDQQITUQAAADCaQpvPWvM4GAAAAAAAMDAJBgUrDgMCGgUAoHgwGAYKKwYBBAGC # NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUbjNwBlb3 # hi5HnXHbBdmAwVPSttowDQYJKoZIhvcNAQEBBQAEggEAMZYXEIkSKntc6lOlmuMI # WW5nhYVWtN5FvVz52UutSzaf9Cays1mBhh6gooC5vfkV8rsK8CrIFZjewM7+sbHc # XnuYC1aT0HY6nk2B9q9+jcu0W59iM1GEY5gErCl5Ue01KhKIiPlpis5l3sBviFqJ # kIBmIbWzav2UaqtdSnEzsMxKH3nPN1wcIKQDQhGZ0UoQmZLB6wnYMSAKUyV7YGJW # aWX+jxTuK5AkXuLx0K5FuQLQlK5+9ZwN0Ecey2qftV4Kv3e19f+SiPehhSoXGcLt # OsO9MhSr0jT8bpD/y8tfByxZ/usgXyk9YqKVy7KNKsfsER6crO4msx6jtPTjOcFs # 7w== # SIG # End signature block |