├── .github
└── workflows
│ └── stale-issues.yml
├── .vscode
└── launch.json
├── CODE_OF_CONDUCT - sp-MX.md
├── CODE_OF_CONDUCT.md
├── Getting-Started
├── 00-PowerShell-or-ISE - sp-MX.md
├── 00-PowerShell-or-ISE.md
├── 01-Common-Errors - sp-MX.md
├── 01-Common-Errors.md
├── 02-Manual-Installation -sp-MX.md
├── 02-Manual-Installation.md
├── README - sp-MX.md
├── README.md
└── images
│ ├── MilestonePSTools-nupkg-contents.png
│ ├── PowerShell-ISE.png
│ ├── Start-Menu.png
│ ├── VSCode.png
│ └── Windows-PowerShell.png
├── LICENSE
├── LICENSE - sp-MX
├── README - sp-MX.md
├── README.md
├── Samples
├── Active-PTZ-Preset-Positions
│ ├── Invoke-PtzPreset.ps1
│ ├── README sp-MX.md
│ └── README.md
├── AddRemoveViewLayouts
│ ├── Add-VmsViewLayout.ps1
│ └── Remove-VmsViewLayout.ps1
├── Add_Hardware_from_CSV.ps1
├── Adding_Hardware_with_Universal_Driver.ps1
├── BackupMediaDb
│ ├── BackupMediaDb.psm1
│ ├── Public
│ │ ├── Backup-Bank.ps1
│ │ ├── Backup-MediaDb.ps1
│ │ └── Backup-Storage.ps1
│ ├── README sp-MX.md
│ └── README.md
├── Daily-LPR-Exports
│ └── SetupDailyLPRExports.ps1
├── Extend_Get-CameraReport.ps1
├── Find-XProtectDevice.ps1
├── General_Login_Script.ps1
├── Get-CameraConnectivityReport.ps1
├── Get-CameraGroupDeviceCount.ps1
├── Get-CameraReport.ps1
├── Get-DevicePermissionsByRole.ps1
├── Get-ItemState.ps1
├── Get-RecorderProperties.ps1
├── Get-UsersInRoles.ps1
├── Get-VmsCameraPosition.ps1
├── Group-CamerasByModel.ps1
├── Import-GPS-Coordinates
│ ├── Import-GpsCoordinates.png
│ ├── Import-GpsCoordinates.ps1
│ └── README.md
├── ImportFromCsvWithPermissions.ps1
├── ReportOnTransactSources.ps1
├── Reporting
│ ├── Get-RecorderReport.ps1
│ ├── Get-RoleReport.ps1
│ ├── Get-VmsCameraDiskUsage.ps1
│ ├── README - sp-MX.md
│ └── README.md
├── Rules
│ ├── Get-VmsRule.ps1
│ ├── README sp-MX.md
│ ├── README.md
│ └── Remove-VmsRule.ps1
├── Scheduled-Video-Export
│ ├── README sp-MX.md
│ ├── README.md
│ └── ScheduledVideoExport.png
├── ScheduledCameraReport
│ ├── README sp-MX.md
│ ├── README.md
│ ├── ScheduledCameraReport.ps1
│ └── ScheduledCameraReport_screenshot.png
├── ScheduledLogExport
│ ├── README sp-MX.md
│ ├── README.md
│ ├── ScheduledLogExport.ps1
│ └── ScheduledLogExport_screenshot.png
├── Set-AdaptiveStreaming.ps1
├── Set-AxisCameraSettings.ps1
├── Set-HttpsEnabled.ps1
├── Set-MilestoneNames.ps1
├── Snapshots-On-Interval
│ ├── README - sp-MX.md
│ ├── README.md
│ └── setup.ps1
├── Test-DataPresence
│ ├── README - sp-MX.md
│ ├── README.md
│ └── Test-DataPresence.ps1
└── Test-VmsBestPractices.ps1
└── images
├── logo.png
└── screenshot.png
/.github/workflows/stale-issues.yml:
--------------------------------------------------------------------------------
1 | name: 'Close stale issues and PRs'
2 | on:
3 | schedule:
4 | - cron: '30 1 * * *'
5 |
6 | jobs:
7 | stale:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | issues: write
11 | pull-requests: write
12 | steps:
13 | - uses: actions/stale@v9
14 | with:
15 | stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
16 | close-issue-message: 'This issue is being closed due to inactivity. If the issue is not resolved, please open a new issue with a reference back to this issue number.'
17 | days-before-stale: 30
18 | days-before-close: 5
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "PowerShell: Launch Current File",
9 | "type": "PowerShell",
10 | "request": "launch",
11 | "script": "${file}",
12 | "cwd": "${file}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT - sp-MX.md:
--------------------------------------------------------------------------------
1 | # Código de conducta del pacto del colaborador
2 |
3 | ## Nuestro compromiso
4 |
5 | Con el interés de fomentar un entorno abierto y acogedor, nosotros, como contribuyentes y mantenedores, nos comprometemos a hacer de la participación en nuestro proyecto y nuestra comunidad una experiencia libre de acoso para todos, independientemente de su edad, tamaño corporal, discapacidad, etnia, identidad y expresión de género, nivel de experiencia, nacionalidad, apariencia personal, raza, religión, identidad y orientación sexual, o inclinaciones sexuales entre adultos que consienten.
6 |
7 | ## Nuestros estándares
8 |
9 | Ejemplos de comportamiento que contribuye a crear un entorno positivo.
10 | incluir:
11 |
12 | * Usar un lenguaje acogedor e inclusivo
13 | * Ser respetuoso con los diferentes puntos de vista y experiencias.
14 | * Aceptando con gracia las críticas constructivas
15 | * Centrarse en lo que es mejor para la comunidad
16 | * Mostrar empatía hacia otros miembros de la comunidad.
17 |
18 | Ejemplos de comportamiento inaceptable por parte de los participantes incluyen:
19 |
20 | * El uso de lenguaje o imágenes sexualizadas y atención o insinuaciones sexuales no deseadas.
21 | * Trolking, comentarios insultantes / despectivos y ataques personales o políticos.
22 | * Acoso público o privado
23 | * Publicar información privada de otros, como una información física o electrónica.
24 | dirección, sin permiso explícito
25 | * Otra conducta que razonablemente podría considerarse inapropiada en un
26 | entorno profesional
27 |
28 | ## Nuestras responsabilidades
29 |
30 | Los encargados del mantenimiento del proyecto son responsables de aclarar los estándares de comportamiento aceptable y se espera que tomen las medidas correctivas adecuadas y justas en respuesta a cualquier caso de comportamiento inaceptable.
31 |
32 | Los encargados del mantenimiento del proyecto tienen el derecho y la responsabilidad de eliminar, editar o rechazar comentarios, confirmaciones, códigos, ediciones de wiki, problemas y otras contribuciones que no estén alineadas con este Código de Conducta, o prohibir temporal o permanentemente a cualquier colaborador por otros comportamientos que lo consideran inapropiado, amenazante, ofensivo o dañino.
33 |
34 | ## Alcance
35 |
36 | Este Código de Conducta se aplica tanto dentro de los espacios del proyecto como en los espacios públicos cuando una persona representa el proyecto o su comunidad. Ejemplos de representación de un proyecto o comunidad incluyen el uso de una dirección de correo electrónico oficial del proyecto, la publicación a través de una cuenta oficial de redes sociales o la actuación como representante designado en un evento en línea o fuera de línea. Los encargados del mantenimiento del proyecto pueden definir y aclarar más la representación de un proyecto.
37 |
38 | ## Ejecución
39 |
40 | Los casos de comportamiento abusivo, acosador o inaceptable de otro modo pueden informarse comunicándose con el equipo del proyecto en [jh@milestone.us] (mailto: jh@milestone.us). Todas las quejas serán revisadas e investigadas y resultarán en una respuesta que
41 | se considera necesario y apropiado a las circunstancias. El equipo del proyecto está obligado a mantener la confidencialidad con respecto al informante de un incidente.
42 |
43 | Se pueden publicar más detalles de las políticas de aplicación específicas por separado.
44 |
45 | Los mantenedores del proyecto que no sigan o hagan cumplir el Código de Conducta de buena fe pueden enfrentar repercusiones temporales o permanentes según lo determinen otros miembros del liderazgo del proyecto.
46 |
47 | ## Atribución
48 |
49 | Este Código de conducta está adaptado del [Pacto del colaborador] [página de inicio], versión 1.4.1, disponible en [http://contributor-covenant.org/version/1/4/1][version]
50 |
51 | [página de inicio]: http://contributor-covenant.org
52 | [versión]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at jh@milestonesys.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/Getting-Started/00-PowerShell-or-ISE - sp-MX.md:
--------------------------------------------------------------------------------
1 | # PowerShell o ISE?
2 |
3 | En un sistema operativo Windows estándar, normalmente tiene dos opciones para usar PowerShell. Tiene más opciones si considera las variantes x86 (32 bits). Cuando está comenzando, puede ser difícil saber cuál usar y porqué. Cuando hace clic en el botón del menú Inicio o presiona su tecla de Windows (⊞), y escribe "powershell", esto es lo que obtiene...
4 |
5 | 
6 |
7 | ## PowerShell de Windows
8 |
9 | Esta es su opción de acceso para ingresar un comando a la vez para realizar una tarea simple y única. No es ideal para escribir secuencias de comandos más largos y complejas, ya que cada vez que presione Enter evaluará lo que ha escrito y lo ejecutará. A menudo uso el terminal de Windows PowerShell para ejecutar comandos individuales como `ping`, y `Test-NetConnection`, o en ocasiones para tareas únicas que requieren varios comandos que me siento cómodo ejecutando en una terminal en lugar de un editor como ISE.
10 |
11 | 
12 |
13 | ## Windows PowerShell ISE
14 |
15 | El Entorno de Scripting Integrado (ISE) PowerShell se incluye en todas las versiones de Windows y proporciona un entorno fácil de usar para escribir scripts en un editor de texto y ejecutar esas secuencias de comandos en la misma interfaz. Aquí es donde desea estar si desea crear una secuencia de comandos de PowerShell. Puede utilizar el Bloc de notas para escribir un archivo .PS1, pero PowerShell ISE ofrece tabulación completa e "Intellisense". Intellisense es como un compañero de desarrollador que conoce todos los parámetros disponibles para cualquier comando que esté escribiendo, por lo que tan pronto como escriba "-" después de `Get-ChildItem` le mostrará todos los parámetros disponibles.
16 |
17 | También puede ejecutar una o más líneas de código a la vez con F8 o ejecutar todo el archivo con F5. Cuando se sienta cómodo con PowerShell como lenguaje y el entorno ISE, incluso puede agregar puntos de interrupción y *depurar* sus secuencias de comandos cuando lo hagan de forma inesperada.
18 |
19 | Hay mejores entornos para escribir código de PowerShell que el ISE. Por ejemplo, Visual Studio Code es un editor *gratuito* de Microsoft con extensiones para PowerShell que lo convierten en un entorno mucho más productivo para proyectos de PowerShell más grandes. Sin embargo, el ISE está disponible en *todos* los equipos con Windows y ofrece el punto de partida menos intimidante para la ruta de aprendizaje de PowerShell.
20 |
21 | 
22 |
23 | ## Windows PowerShell (x86) y Windows PowerShell ISE (x86)
24 |
25 | Estos son los equivalentes de 32 bits de los mismos dos entornos de PowerShell ya mencionados. Los entornos estándar de PowerShell son de 64 bits y rara vez necesitará un entorno de 32 bits, pero si lo necesita, lo tiene. Puesto que el MIP SDK de Milestone se proporciona principalmente como paquetes NuGet de 64 bits y el MIP SDK de 32 bits está en desuso, necesitará un entorno Windows PowerShell 5.1 de 64 bits para usar el módulo MilestonePSTools PowerShell.
26 |
27 | ## Visual Studio Code
28 |
29 | VSCode de Microsoft es un entorno fantástico para trabajar en muchos tipos de proyectos, desde PowerShell hasta HTML/CSS/JavaScript, Python y más. Es un editor de texto con extensiones que lo convierten en un entorno cómodo para trabajar con múltiples archivos de diferentes tipos, e incluso ejecutar código. Es una parte integral del mantenimiento de MilestonePSTools y otros proyectos de PowerShell en los que hemos trabajado y, a medida que se sienta más cómodo con PowerShell, le recomiendo que lo pruebe. La siguiente secuencia de comandos automatizará la instalación de código, así como mis extensiones favoritas para trabajar con PowerShell y GitHub.
30 |
31 | 
32 |
33 | ```powershell
34 | $InformationPreference = 'Continue'
35 | $requestParams = @{
36 | Uri = "https://code.visualstudio.com/sha/download?build=stable&os=win32-x64"
37 | OutFile = Join-Path $env:TEMP VSCodeUserSetup.exe
38 | }
39 | Write-Information "Downloading VSCode from $($requestParams.Uri)"
40 | Invoke-WebRequest @requestParams
41 |
42 | if (-not (Test-Path -Path $requestParams.OutFile)) {
43 | throw "Could not find the downloaded installer at $($requestParams.OutFile)"
44 | }
45 |
46 | Write-Information 'Installing VSCode from $($requestParams.OutFile). . .'
47 | $installerArgs = @{
48 | FilePath = $requestParams.OutFile
49 | Wait = $true
50 | NoNewWindow = $true
51 | PassThru = $true
52 | ErrorAction = 'Stop'
53 | ArgumentList = @(
54 | '/verysilent',
55 | '/suppressmsgboxes',
56 | '/mergetasks="!runCode, desktopicon, quicklaunchicon, addcontextmenufiles, addcontextmenufolders, associatewithfiles, addtopath"'
57 | )
58 | }
59 | $result = Start-Process @installerArgs
60 | Remove-Item -Path $requestParams.OutFile -Force
61 | if ($result.ExitCode -notin @(0, 1641, 3010)) {
62 | throw "VSCode installer exited with code $($result.ExitCode)"
63 | }
64 | $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
65 | Write-Information "Success! VSCode version $(code --version)"
66 |
67 | Write-Information "Installing a couple important VSCode extensions. Some other fun ones include Rainbow Brackets, indent-rainbox, Live Share*, and markdownlint."
68 | $extensions = @(
69 |
70 | )
71 | $extensions = @(
72 | 'ms-vscode.powershell',
73 | 'github.vscode-pull-request-github',
74 | 'davidanson.vscode-markdownlint',
75 | 'usernamehw.errorlens'
76 | )
77 | $extensions | Foreach-Object { code --install-extension $_ --force }
78 |
79 | Write-Information 'Done! Type "code" to open VSCode or "code ." to open the current directory in VSCode.'
80 | ```
81 |
--------------------------------------------------------------------------------
/Getting-Started/00-PowerShell-or-ISE.md:
--------------------------------------------------------------------------------
1 | # PowerShell or ISE?
2 |
3 | On an standard Windows operating system you typically have two choices for how to use PowerShell. More if you consider the x86 (32-bit) variants! When you're getting started, it's difficult to know which to use, and why. When you click the Start menu button or press your Windows key (⊞), and type "powershell", here's what you get...
4 |
5 | 
6 |
7 | ## Windows PowerShell
8 |
9 | This is your go-to option for entering one command at a time to perform a simple, one-time task. It's not great for writing out longer, complex scripts since every time you press Enter it will evaluate what you've typed and run it. I often use the Windows PowerShell terminal to run single commands like `ping`, and `Test-NetConnection`, or sometimes for one-off tasks that require multiple commands that I'm comfortable running in a terminal instead of an editer like ISE.
10 |
11 | 
12 |
13 | ## Windows PowerShell ISE
14 |
15 | The PowerShell Integrated Scripting Environment (ISE) is included in all versions of Windows and provides you with a user-friendly environment to write scripts in a text editor, and run those scripts in the same interface. This is where you want to be if you want to craft a PowerShell script. You can use Notepad to write a .PS1 file, but PowerShell ISE offers tab-completion and "Intellisense". Intellisense is like a developer side-kick who knows all the parameters available for whatever command you're writing, so as soon as you type "-" after `Get-ChildItem` it will show you all the available parameters you can use.
16 |
17 | You can also run one or more lines of code at a time using F8 or run the whole file using F5. When you get comfortable with PowerShell as a language, and the ISE environment, you can even add break points and *debug* your scripts when they do the unexpected.
18 |
19 | There are better environments to write PowerShell code in than the ISE. For instance, Visual Studio Code is a *free* editor from Microsoft with extensions for PowerShell which make it a far more productive environment for larger PowerShell projects. However, the ISE is available on *every* Windows computer and offers the least intimidating starting point for your PowerShell learning path.
20 |
21 | 
22 |
23 | ## Windows PowerShell (x86) and Windows PowerShell ISE (x86)
24 |
25 | These are the 32-bit equivalents of the same two PowerShell environments already mentioned. The standard PowerShell environments are 64-bit and you will rarely need a 32-bit environment but if you need it, you have it. Since the Milestone MIP SDK is provided primarily as 64-bit NuGet packages and the 32-bit MIP SDK is deprecated, you will require a 64-bit Windows PowerShell 5.1 environment to use the MilestonePSTools PowerShell module.
26 |
27 | ## Visual Studio Code
28 |
29 | Microsoft's VSCode is a fantastic environment for working on many different kinds of projects from PowerShell, to HTML/CSS/JavaScript, to Python and more. It's a text editor with extensions which make it a comfortable environment for working with multiple files of different types, and even running/executing code. It's an integral part of the maintenance of MilestonePSTools and other PowerShell projects we've worked on and as you get more comfortable with PowerShell, I highly recommend trying it out. The script below will automate the installation of code, as well as my favorite extensions for working with PowerShell and GitHub.
30 |
31 | 
32 |
33 | ```powershell
34 | $InformationPreference = 'Continue'
35 | $requestParams = @{
36 | Uri = "https://code.visualstudio.com/sha/download?build=stable&os=win32-x64"
37 | OutFile = Join-Path $env:TEMP VSCodeUserSetup.exe
38 | }
39 | Write-Information "Downloading VSCode from $($requestParams.Uri)"
40 | Invoke-WebRequest @requestParams
41 |
42 | if (-not (Test-Path -Path $requestParams.OutFile)) {
43 | throw "Could not find the downloaded installer at $($requestParams.OutFile)"
44 | }
45 |
46 | Write-Information 'Installing VSCode from $($requestParams.OutFile). . .'
47 | $installerArgs = @{
48 | FilePath = $requestParams.OutFile
49 | Wait = $true
50 | NoNewWindow = $true
51 | PassThru = $true
52 | ErrorAction = 'Stop'
53 | ArgumentList = @(
54 | '/verysilent',
55 | '/suppressmsgboxes',
56 | '/mergetasks="!runCode, desktopicon, quicklaunchicon, addcontextmenufiles, addcontextmenufolders, associatewithfiles, addtopath"'
57 | )
58 | }
59 | $result = Start-Process @installerArgs
60 | Remove-Item -Path $requestParams.OutFile -Force
61 | if ($result.ExitCode -notin @(0, 1641, 3010)) {
62 | throw "VSCode installer exited with code $($result.ExitCode)"
63 | }
64 | $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
65 | Write-Information "Success! VSCode version $(code --version)"
66 |
67 | Write-Information "Installing a couple important VSCode extensions. Some other fun ones include Rainbow Brackets, indent-rainbox, Live Share*, and markdownlint."
68 | $extensions = @(
69 |
70 | )
71 | $extensions = @(
72 | 'ms-vscode.powershell',
73 | 'github.vscode-pull-request-github',
74 | 'davidanson.vscode-markdownlint',
75 | 'usernamehw.errorlens'
76 | )
77 | $extensions | Foreach-Object { code --install-extension $_ --force }
78 |
79 | Write-Information 'Done! Type "code" to open VSCode or "code ." to open the current directory in VSCode.'
80 | ```
81 |
--------------------------------------------------------------------------------
/Getting-Started/01-Common-Errors - sp-MX.md:
--------------------------------------------------------------------------------
1 | # Common Errors
2 |
3 | La secuencia de comandos de instalación proporcionada en el archivo README.md de este proyecto está diseñado para protegerlo de errores comunes cuando instala cosas de la Galería de PowerShell por primera vez. A continuación se muestra una colección de estos errores, por lo que si se encuentra con ellos, sepa lo que significan y qué debe hacer.
4 |
5 | ## No se encontró ninguna coincidencia para los criterios de búsqueda y el nombre del módulo especificados
6 |
7 | No se encontró ninguna coincidencia para los criterios de búsqueda y el nombre del módulo especificados
8 |
9 | ```powershell
10 | PS C:\> Install-Module MilestonePSTools
11 | WARNING: Unable to resolve package source 'https://www.powershellgallery.com/api/v2'.
12 | PackageManagement\Install-Package : No match was found for the specified search criteria and module name
13 | 'MilestonePSTools'. Try Get-PSRepository to see all available registered module repositories.
14 | At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1809 char:21
15 | + ... $null = PackageManagement\Install-Package @PSBoundParameters
16 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | + CategoryInfo : ObjectNotFound: (Microsoft.Power....InstallPackage:InstallPackage) [Install-Package], Ex
18 | ception
19 | + FullyQualifiedErrorId : NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage
20 | ```
21 |
22 | El problema no es que el nombre del módulo se haya escrito incorrectamente o que no se pueda encontrar en PSGallery. El problema es que PowerShell no pudo *conectarse* a [https://www.powershellgallery.com](https://www.powershellgallery.com) porque PSGallery requiere una conexión HTTPS usando al menos TLS 1.2, y las versiones anteriores de PowerShellGet aún usan una versión anterior de TLS o SSL.
23 |
24 |
25 | ### Solución
26 |
27 | Para resolver esto, necesitamos decirle a PowerShell qué protocolos queremos usar. Una forma de hacerlo es ejecutar la línea bastante esotérica del código de PowerShell a continuación. Utiliza la clase .NET System.Net.ServicePointManager y modifica SecurityProtocol para incluir TLS 1.2 además de los protocolos que ya estén permitidos.
28 |
29 | ```powershell
30 | [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
31 | ```
32 |
33 | Así es como se ve después de ejecutar esto en una instancia limpia de Windows 10 Sandbox:
34 |
35 | ```powershell
36 | PS C:\> [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
37 | PS C:\> [Net.ServicePointManager]::SecurityProtocol
38 | Tls11, Tls12
39 | PS C:\>
40 | ```
41 |
42 | ## El comando se encontró en el módulo, pero el módulo no se pudo cargar
43 |
44 | Esto es muy común en Windows 10 porque la política de ejecución predeterminada de Microsoft en Windows 10 es "Restringida". Esto significa que PowerShell no puede ejecutar *ningún* archivo *.ps1 o *.psm1. Cuando utiliza `Import-Module` para importar MilestonePSTools, o cuando usa un comando dentro del módulo como `Connect-ManagementServer`, lo primero que hace PowerShell es ejecutar el archivo .PSM1 dentro de la carpeta de instalación del módulo. Con una política de ejecución de "Restringida", PowerShell no puede cargar el módulo..
45 |
46 | ```powershell
47 | PS C:\> Connect-ManagementServer -ShowDialog
48 | Connect-ManagementServer : The 'Connect-ManagementServer' command was found in the module 'MilestonePSTools', but the
49 | module could not be loaded. For more information, run 'Import-Module MilestonePSTools'.
50 | At line:1 char:1
51 | + Connect-ManagementServer -ShowDialog
52 | + ~~~~~~~~~~~~~~~~~~~~~~~~
53 | + CategoryInfo : ObjectNotFound: (Connect-ManagementServer:String) [], CommandNotFoundException
54 | + FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
55 | ```
56 |
57 | PowerShell suele ser bastante bueno para brindarle la información que necesita en el mensaje de error. En este caso, le recomienda ejecutar `Import-Module MilestonePSTools` para obtener más información sobre el problema. Así es como se ve eso ...
58 |
59 | ```powershell
60 | PS C:\> Import-Module MilestonePSTools
61 | Import-Module : File C:\Program Files\WindowsPowerShell\Modules\MipSdkRedist\21.1.1\MipSdkRedist.psm1 cannot be loaded
62 | because running scripts is disabled on this system. For more information, see about_Execution_Policies at
63 | https:/go.microsoft.com/fwlink/?LinkID=135170.
64 | At line:1 char:1
65 | + import-module milestonepstools
66 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67 | + CategoryInfo : SecurityError: (:) [Import-Module], PSSecurityException
68 | + FullyQualifiedErrorId : UnauthorizedAccess,Microsoft.PowerShell.Commands.ImportModuleCommand
69 | ```
70 |
71 | Puede ver que el mensaje de error más detallado hace referencia a las políticas de ejecución de manera más específica
72 |
73 | ### Solución
74 |
75 | La forma de solucionarlo es [cambiar su política de ejecución](https:/go.microsoft.com/fwlink/?LinkID=135170).. Recomiendo leer más sobre las políticas de ejecución de Microsoft, ya que no podemos cubrir todo el tema aquí. Nuestra preferencia por una política de ejecución es cambiarla a "RemoteSigned". Esto significa que podrá ejecutar cualquier secuencia de comandos local de PowerShell, pero se requerirá que otras secuencias de comandos, descargadas de un origen de Internet que no sea de confianza, estén firmadas por un certificado de firma de código en el que ya confíe. Puede hacer que una secuencia de comandos ”remota” que no sea de confianza sea “local” y “de confianza” al hacer clic derecho en el archivo y marcando la casilla de verificación “desbloquear” en la parte inferior de la pestaña General. Si no ve esa casilla de verificación, entonces el archivo no está “bloqueado” y Windows debería permitirle ejecutar el archivo siempre que su política de ejecución sea al menos “RemoteSigned”.
76 |
77 | Ejecute el comando `Set-ExecutionPolicy` como administrador para modificar la política en el nivel del equipo. También puede especificar un alcance de "CurrentUser" o "Process", por lo que, si tiene tiempo, le recomiendo leer la base de conocimiento de Microsoft sobre políticas de ejecución para obtener una comprensión completa de las opciones y sus implicaciones.
78 |
79 | ```powershell
80 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
81 | ```
82 |
83 | ## Repositorio que no es de confianza
84 |
85 | Si bien en realidad no es un error, cuando ve este mensaje por primera vez puede ser confuso. Justificadamente, es posible que se pregunte si debe proceder a instalar algo de una fuente que no sea de confianza. El repositorio de PSGallery es la [Galería de PowerShell](https://www.powershellgallery.com) administrada por Microsoft. Es una colección de miles de paquetes que contienen módulos de PowerShell como [MilestonePSTools](https://www.powershellgallery.com/packages/MilestonePSTools). De forma predeterminada, el repositorio de PSGallery incluido con el módulo PowerShellGet integrado no es de confianza. Por lo tanto, se le preguntará cada vez que necesite instalar o actualizar un módulo de este repositorio.
86 |
87 | ```powershell
88 | PS C:\> Install-Module MilestonePSTools
89 |
90 | Untrusted repository
91 | You are installing the modules from an untrusted repository. If you trust this repository, change its
92 | InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
93 | 'PSGallery'?
94 | ```
95 |
96 | ### SoluSolucióntion
97 |
98 | Puede optar por reconocer este mensaje cada vez o puede establecer la `Política de instalación` para el repositorio en "De confianza". A continuación, le indicamos cómo hacerlo:
99 |
100 | ```powershell
101 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
102 | ```
103 |
--------------------------------------------------------------------------------
/Getting-Started/01-Common-Errors.md:
--------------------------------------------------------------------------------
1 | # Common Errors
2 |
3 | The installation script provided in this project's README.md is designed to protect you from common errors when you install things from PowerShell Gallery for the first time. Following are a collection of these errors so if you run into them, you know what it means and what to do!
4 |
5 | ## No match was found for the specified search criteria and module name
6 |
7 | If you receive this error when installing a PowerShell module, there's a good chance you typed the name correctly.
8 |
9 | ```powershell
10 | PS C:\> Install-Module MilestonePSTools
11 | WARNING: Unable to resolve package source 'https://www.powershellgallery.com/api/v2'.
12 | PackageManagement\Install-Package : No match was found for the specified search criteria and module name
13 | 'MilestonePSTools'. Try Get-PSRepository to see all available registered module repositories.
14 | At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1809 char:21
15 | + ... $null = PackageManagement\Install-Package @PSBoundParameters
16 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | + CategoryInfo : ObjectNotFound: (Microsoft.Power....InstallPackage:InstallPackage) [Install-Package], Ex
18 | ception
19 | + FullyQualifiedErrorId : NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage
20 | ```
21 |
22 | The issue is not that the module name was wrong or it couldn't be found on PSGallery. The issue is PowerShell couldn't *connect* to [https://www.powershellgallery.com](https://www.powershellgallery.com) because PSGallery requires an HTTPS connection using at least TLS 1.2, and older versions of PowerShellGet still use an older version of TLS or SSL.
23 |
24 | ### Solution
25 |
26 | To solve this, we need to tell PowerShell which protocol(s) we want to use. One way to do this is to run the rather esoteric line of PowerShell code below. It uses the .NET System.Net.ServicePointManager class and modifies the SecurityProtocol to include TLS 1.2 in addition to whatever protocols are already allowed.
27 |
28 | ```powershell
29 | [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
30 | ```
31 |
32 | Here's what it looks like after I run this on a clean Windows 10 Sandbox instance...
33 |
34 | ```powershell
35 | PS C:\> [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
36 | PS C:\> [Net.ServicePointManager]::SecurityProtocol
37 | Tls11, Tls12
38 | PS C:\>
39 | ```
40 |
41 | ## The command was found in the module, but the module could not be loaded
42 |
43 | This is really common on Windows 10 because Microsoft's default execution policy on Windows 10 is "Restricted". This means PowerShell is not allowed to execute *any* \*.ps1 files or \*.psm1 files. When you use `Import-Module` to import MilestonePSTools, or when you use a command within the module like `Connect-ManagementServer` the first thing PowerShell does is run the .PSM1 file inside the module's installation folder. With an execution policy of "Restricted", PowerShell can't do that.
44 |
45 | ```powershell
46 | PS C:\> Connect-ManagementServer -ShowDialog
47 | Connect-ManagementServer : The 'Connect-ManagementServer' command was found in the module 'MilestonePSTools', but the
48 | module could not be loaded. For more information, run 'Import-Module MilestonePSTools'.
49 | At line:1 char:1
50 | + Connect-ManagementServer -ShowDialog
51 | + ~~~~~~~~~~~~~~~~~~~~~~~~
52 | + CategoryInfo : ObjectNotFound: (Connect-ManagementServer:String) [], CommandNotFoundException
53 | + FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
54 | ```
55 |
56 | PowerShell is usually pretty good at giving you the information you need in the error message. In this case, it recommends you to run `Import-Module MilestonePSTools` to get more information about the problem. Here's what that looks like...
57 |
58 | ```powershell
59 | PS C:\> Import-Module MilestonePSTools
60 | Import-Module : File C:\Program Files\WindowsPowerShell\Modules\MipSdkRedist\21.1.1\MipSdkRedist.psm1 cannot be loaded
61 | because running scripts is disabled on this system. For more information, see about_Execution_Policies at
62 | https:/go.microsoft.com/fwlink/?LinkID=135170.
63 | At line:1 char:1
64 | + import-module milestonepstools
65 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66 | + CategoryInfo : SecurityError: (:) [Import-Module], PSSecurityException
67 | + FullyQualifiedErrorId : UnauthorizedAccess,Microsoft.PowerShell.Commands.ImportModuleCommand
68 | ```
69 |
70 | You can see that the more detailed error more specifically references execution policies.
71 |
72 | ### Solution
73 |
74 | The fix is to [change your execution policy](https:/go.microsoft.com/fwlink/?LinkID=135170). I recommend reading more about execution policies from Microsoft as we can't possibly cover the subject here. Our preference for execution policy is to change it to "RemoteSigned". This means you will be able to run any local PowerShell script, but any script downloaded from an untrusted Internet source will be required to be signed by a code signing certificate you already trust. You can make an untrusted "remote" script "local" and trusted by right-clicking on the file and checking the "unblock" checkbox at the bottom of the General tab. If you don't see that checkbox, then the file is not "blocked" and Windows should let you execute the file so long as your execution policy is at least "RemoteSigned".
75 |
76 | Run the `Set-ExecutionPolicy` command as Administrator to modify the policy at the machine level. You can also specify a scope of "CurrentUser" or "Process" so if you have time, I do recommend reading Microsoft's KB on execution policies to get a full understanding of the options and their implications.
77 |
78 | ```powershell
79 | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
80 | ```
81 |
82 | ## Untrusted repository
83 |
84 | While not an error, when you see this message for the first time it can be confusing. Justifiably, you may wonder whether you should proceed to install something from an untrusted source. The PSGallery repository is the [PowerShell Gallery](https://www.powershellgallery.com) managed by Microsoft. It is a collection of thousands of packages containing PowerShell modules like [MilestonePSTools](https://www.powershellgallery.com/packages/MilestonePSTools). By default, the PSGallery repository included with the built-in PowerShellGet module is not trusted. So you will be asked each time you need to install or update a module from this repository.
85 |
86 | ```powershell
87 | PS C:\> Install-Module MilestonePSTools
88 |
89 | Untrusted repository
90 | You are installing the modules from an untrusted repository. If you trust this repository, change its
91 | InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
92 | 'PSGallery'?
93 | ```
94 |
95 | ### Solution
96 |
97 | You can either choose to acknowledge this message each time, or you can set the `InstallationPolicy` for the repository to "Trusted". Here's how to do that...
98 |
99 | ```powershell
100 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
101 | ```
102 |
--------------------------------------------------------------------------------
/Getting-Started/02-Manual-Installation -sp-MX.md:
--------------------------------------------------------------------------------
1 | # Instalación manual
2 |
3 | Si su VMS de Milestone está "aislado" o por cualquier otra razón no puede instalar un módulo de PowerShell con el cmdlet `Install-Module` que lo descarga directamente desde la Galería de PowerShell, aún puede instalar MilestonePSTools. Síganos para aprender cómo.
4 |
5 | ## Descargar los archivos nupkg
6 |
7 | ¿Qué se supone que es un archivo nupkg? Para empezar, puedes pronunciarlo “nup-keg”, ¡lo cual es divertido! Y significa “Paquete NuGet”. NuGet es el nombre del administrador de paquetes de Microsoft introducido principalmente para administrar paquetes de aplicaciones .NET. En este caso, "paquete" significa uno o más archivos DLL y algunas instrucciones básicas para dónde van. Antes de 2010, la mayoría de los desarrolladores de .NET copiaban manualmente alrededor de archivos DLL y agregaban referencias a ellos cuando era necesario. Hizo que fuera muy complicado compartir bibliotecas reutilizables. Ahora, con NuGet.org, puede hacer referencia a un paquete por su nombre y descargar/desempaquetar/usar automáticamente ese paquete.
8 |
9 | Para descargar manualmente MilestonePSTools, deberá descargar dos archivos. El primero es el "archivo nupgk sin formato" MilestonePSTools y el segundo es el MipSdkRedist nupkg. El módulo MipSdkRedist es el contenedor utilizado para el MIP SDK de Milestone en el que se basa MilestonePSTools. Estos son los vínculos a los dos módulos de PowerShell en PSGallery. Una vez allí, haga clic en **Descarga manual** en **Opciones de instalación** y luego haga clic en **Descargar el archivo nupkg sin procesar**.
10 |
11 | - [MilestonePSTools](https://www.powershellgallery.com/packages/MilestonePSTools)
12 | - [MipSdkRedist](https://www.powershellgallery.com/packages/MipSdkRedist)
13 |
14 | Estos archivos nupkg son en realidad archivos ZIP. Si agrega la extensión .zip al archivo, puede ver/extraer el contenido como cualquier otro archivo zip. Así es como se ven los contenidos de MilestonePSTools:
15 |
16 | 
17 |
18 | Antes de extraer los archivos ZIP, asegúrese de hacer clic derecho en ambos archivos y abrir **Propiedades**. Si ve una casilla de verificación para "desbloquear" los archivos, debe hacerlo antes de extraerlos. De lo contrario, *también* se bloqueará cada archivo extraído individualmente.
19 |
20 | Al extraer los archivos del módulo, el mejor lugar para colocarlos es en una de las ubicaciones en las que PowerShell busca *automáticamente* los módulos de PowerShell. Si instala el módulo *solo para usted*, debe colocar el módulo en su directorio Documentos en `~\Documents\WindowsPowerShell\Modules`. Es posible que las carpetas no existan todavía. De ser así, está bien que las cree usted mismo.
21 |
22 | Como alternativa, si desea que los módulos estén disponibles para cualquier usuario de un equipo local (útil si desea que una cuenta de servicio, un sistema local o un servicio de red acceda a ellos desde una tarea programada), puede colocarlos en `C:\Program Files\WindowsPowerShell\Modules.`
23 |
24 | En la estructura de la carpeta Módulos, el primer nivel incluye una carpeta que coincide con el nombre del módulo, y la subcarpeta contiene una o más versiones de ese módulo donde el nombre de la carpeta coincide con la versión exacta del módulo tal como se define en el archivo `*.psd1` en la raíz de la carpeta del módulo específico. En el siguiente ejemplo, tenemos MilestonePSTools versión 21.1.451603, y dentro de esa carpeta están los contenidos de la captura de pantalla anterior, de modo que MilestonePSTools.psd1 existe dentro de la carpeta llamada "21.1.451603".
25 |
26 | ```text
27 | +---Modules
28 | +---MilestonePSTools
29 | | \---21.1.451603
30 | +---MipSdkRedist
31 | | \---21.1.1
32 | ```
33 |
34 | Una vez que haya extraído los módulos y los haya colocado en la ubicación correcta, debería poder ejecutar `Import-Module MilestonePSTools` y tanto MipSdkRedist como MilestonePSTools se cargarán en su sesión de PowerShell. Si recibe un mensaje de error, consulte [01-CommonErrors](01-CommonErrors.md) para ver si ya hemos compartido algunos consejos sobre cómo solucionarlo.
--------------------------------------------------------------------------------
/Getting-Started/02-Manual-Installation.md:
--------------------------------------------------------------------------------
1 | # Manual Installation
2 |
3 | If your Milestone VMS is "air-gapped" or for any other reason you're unable to install a PowerShell module using the `Install-Module` cmdlet which downloads it directly from PowerShell Gallery, you can still install MilestonePSTools! Follow along to learn how.
4 |
5 | ## Download the Nupkg files
6 |
7 | What on earth is a nupkg file? For starters, you can pronounce it as "Nup-keg" which is fun! And it stands for "NuGet Package". Oh, and NuGet is the name of Microsoft's package manager introduced primarily for managing .NET application packages. In this case, "package" means one or more DLL files and some basic instructions for where they go. Back before ~2010, most .NET developers were manually copying around DLL files and adding references to them when needed. It made it very complicated to share reusable libraries. Now, with NuGet.org, you can reference a package by name, and automatically download/unpack/use that package.
8 |
9 | To manually download MilestonePSTools, you'll need to download two files. The first is the MilestonePSTools "raw nupgk file", and the second is the MipSdkRedist nupkg. The MipSdkRedist module is the container used for the Milestone MIP SDK on which MilestonePSTools is based. Here are the links to the two PowerShell modules on PSGallery. Once there, click **Manual Download** under **Installation Options** and then click **Download the raw nupkg file**.
10 |
11 | - [MilestonePSTools](https://www.powershellgallery.com/packages/MilestonePSTools)
12 | - [MipSdkRedist](https://www.powershellgallery.com/packages/MipSdkRedist)
13 |
14 | These nupkg files are actually ZIP files! If you add the `.zip` extension to the file, you can view/extract the contents like any other zip file. Here's what the contents look like for MilestonePSTools...
15 |
16 | 
17 |
18 | Before you extract the ZIP files, make sure to right-click on both files and open **Properties**. If you see a checkbox to "unblock" the files, you should do this before extracting them. Otherwise each individual extracted file will *also* be blocked.
19 |
20 | When you extract the files for the module, the best place to put them is in one of the locations PowerShell *automatically* looks for PowerShell modules. If you install the module for *just you*, then you should place the module in your Documents directory under `~\Documents\WindowsPowerShell\Modules`. The folder(s) may not already exist. If so, it is okay to create them yourself.
21 |
22 | Alternatively if you want to make the module(s) available to any user on the local machine (useful if you want a service account, local system, or network service to access them from a scheduled task!), you can place them in `C:\Program Files\WindowsPowerShell\Modules`.
23 |
24 | The structure for the Modules folder is that the first level includes a folder matching the name of the module, and the subfolder contains one or more versions of that module where the name of the folder matches the exact version of the module as defined in the `*.psd1` file at the root of the specific module's folder. In the example below, we have MilestonePSTools version 21.1.451603, and inside that folder are the contents from the screenshot above such that MilestonePSTools.psd1 exists inside the folder named "21.1.451603".
25 |
26 | ```text
27 | +---Modules
28 | +---MilestonePSTools
29 | | \---21.1.451603
30 | +---MipSdkRedist
31 | | \---21.1.1
32 | ```
33 |
34 | Once you have the modules extracted and placed in the right location, you should be able to run `Import-Module MilestonePSTools` and both MipSdkRedist and MilestonePSTools will be loaded into your PowerShell session. If you get an error message, check out [01-CommonErrors](01-CommonErrors.md) to see if we've already shared some tips on how to deal with it!
35 |
--------------------------------------------------------------------------------
/Getting-Started/README - sp-MX.md:
--------------------------------------------------------------------------------
1 | # Introducción (MÁS)
2 |
3 | Si es nuevo en PowerShell, el archivo principal README de este proyecto es mucho para digerir de inmediato. Los temas de esta carpeta están aquí cuando esté listo para responder a preguntas y errores comunes.
4 |
5 | Los primeros temas tratan sobre cómo comenzar con PowerShell y no necesariamente se aplican específicamente a Milestone. Después de eso, comenzaremos a explorar tareas comunes, comenzando con conectarse a su sistema Milestone y ejecutar comandos e informes comunes. A continuación, exploraremos algunas tareas más avanzadas, como agregar y configurar hardware y explorar algunos de los cmdlets menos utilizados.
6 |
7 | Si tiene tiempo, lea los temas de esta carpeta y explore los ejemplos. La carpeta Ejemplos en la raíz de este repositorio es otro buen lugar para buscar ejemplos sin procesar de cómo escribir funciones y realizar tareas en un sistema Milestone. Esta carpeta tiene como objetivo ofrecer una introducción suave a su experiencia PowerShell/MilestonePSTools.
8 |
--------------------------------------------------------------------------------
/Getting-Started/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started (MORE)
2 |
3 | If you are new to PowerShell, the main README on this project is a lot to digest all at once. The topics in this folder are here for when you're ready for answers to common questions and errors.
4 |
5 | The first few topics are specifically about getting started with PowerShell and don't necessarily apply to Milestone at all. After that, we'll start exploring common tasks beginning with getting connected to your Milestone system, running common commands and reports, and then some more advanced tasks like adding and configuring hardware and exploring some of the lesser-used cmdlets.
6 |
7 | If you have the time, read through the topics in this folder and explore the examples. The Samples folder at the root of this repository are another good place to look for raw examples of how to write functions and perform tasks against a Milestone system. This folder aims to offer a more gentle approach to your PowerShell/MilestonePSTools experience.
8 |
--------------------------------------------------------------------------------
/Getting-Started/images/MilestonePSTools-nupkg-contents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Getting-Started/images/MilestonePSTools-nupkg-contents.png
--------------------------------------------------------------------------------
/Getting-Started/images/PowerShell-ISE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Getting-Started/images/PowerShell-ISE.png
--------------------------------------------------------------------------------
/Getting-Started/images/Start-Menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Getting-Started/images/Start-Menu.png
--------------------------------------------------------------------------------
/Getting-Started/images/VSCode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Getting-Started/images/VSCode.png
--------------------------------------------------------------------------------
/Getting-Started/images/Windows-PowerShell.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Getting-Started/images/Windows-PowerShell.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Milestone Systems Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE - sp-MX:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Milestone Systems Inc.
2 |
3 | Por la presente se concede permiso, libre de cargos, a cualquier persona que obtenga una copia de este software y de los archivos de documentación asociados (el "Software"), a utilizar el Software sin restricción, incluyendo sin limitación los derechos a usar, copiar, modificar, fusionar, publicar, distribuir, sublicenciar, y/o vender copias del Software, y a permitir a las personas a las que se les proporcione el Software a hacer lo mismo, sujeto a las siguientes condiciones:
4 |
5 | El aviso de copyright anterior y este aviso de permiso se incluirán en todas las copias o partes sustanciales del Software.
6 |
7 | EL SOFTWARE SE PROPORCIONA "COMO ESTA", SIN GARANTÍA DE NINGÚN TIPO, EXPRESA O IMPLÍCITA, INCLUYENDO PERO NO LIMITADO A GARANTÍAS DE COMERCIALIZACIÓN, IDONEIDAD PARA UN PROPÓSITO PARTICULAR E INCUMPLIMIENTO. EN NINGÚN CASO LOS AUTORES O PROPIETARIOS DE LOS DERECHOS DE AUTOR SERÁN RESPONSABLES DE NINGUNA RECLAMACIÓN, DAÑOS U OTRAS RESPONSABILIDADES, YA SEA EN UNA ACCIÓN DE CONTRATO, AGRAVIO O CUALQUIER OTRO MOTIVO, DERIVADAS DE, FUERA DE O EN CONEXIÓN CON EL SOFTWARE O SU USO U OTRO TIPO DE ACCIONES EN EL SOFTWARE.
8 |
--------------------------------------------------------------------------------
/Samples/Active-PTZ-Preset-Positions/Invoke-PtzPreset.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-PtzPreset {
2 | <#
3 | .SYNOPSIS
4 | Send a command to activate the specified PtzPreset resulting in the associated PTZ camera
5 | moving to the designated coordinates.
6 |
7 | .DESCRIPTION
8 | Send a command to activate the specified PtzPreset resulting in the associated PTZ camera
9 | moving to the designated coordinates.
10 |
11 | The PtzPreset parameter should reference an existing PTZ preset position configured in the
12 | VMS. This cmdlet cannot move a PTZ camera to an arbitratry user-defined coordinate.
13 |
14 | .PARAMETER PtzPreset
15 | The PtzPreset configuration item found under $camera.PtzPresetFolder.PtzPresets
16 |
17 | .PARAMETER VerifyCoordinates
18 | Wait for the camera to arrive at the designated PtzPreset coordinates. Only applies if the
19 | camera uses absolute PTZ positioning.
20 |
21 | .PARAMETER Tolerance
22 | Default: 0.001. Specifies the tolerance for PTZ coordinates as some cameras may not arrive
23 | at the exact coordinates.
24 |
25 | .PARAMETER Timeout
26 | Default: 5 seconds. Specifies the time in seconds to wait for the camera to arrive at the
27 | PtzPreset position. Only applies when the VerifyCoordinates switch is provided, and only
28 | for cameras using absolute PTZ positioning.
29 |
30 | Cameras with relative positioning, or calling this cmdlet without the VerifyCoordinates switch
31 | will result in an immediate return without waiting for the camera to complete it's movement.
32 |
33 | .EXAMPLE
34 | Invoke-PtzPreset -PtzPreset $ptzPreset -VerifyCoordinates
35 |
36 | Calls the $ptsPreset position and instructs the cmdlet to verify the camera has arrived at
37 | the designated position.
38 | #>
39 | [CmdletBinding()]
40 | param (
41 | [Parameter(Mandatory)]
42 | [VideoOS.Platform.ConfigurationItems.PtzPreset]
43 | $PtzPreset,
44 |
45 | [Parameter()]
46 | [switch]
47 | $VerifyCoordinates,
48 |
49 | [Parameter()]
50 | [double]
51 | $Tolerance = 0.001,
52 |
53 | [Parameter()]
54 | [int]
55 | $Timeout = 5
56 | )
57 |
58 | process {
59 | $cameraId = if ($PtzPreset.ParentItemPath -match 'Camera\[(.{36})\]') {
60 | $Matches[1]
61 | }
62 | else {
63 | Write-Error "Could not parse camera ID from ParentItemPath value '$($PtzPreset.ParentItemPath)'"
64 | return
65 | }
66 |
67 | $camera = Get-Camera -Id $cameraId
68 | $cameraItem = $camera | Get-PlatformItem
69 | $presetItem = [VideoOS.Platform.Configuration]::Instance.GetItem([guid]::new($PtzPreset.Id), [VideoOS.Platform.Kind]::Preset)
70 |
71 | $params = @{
72 | MessageId = 'Control.TriggerCommand'
73 | DestinationEndpoint = $presetItem.FQID
74 | UseEnvironmentManager = $true
75 | }
76 | Send-MipMessage @params
77 |
78 | if (-not $VerifyCoordinates) {
79 | return
80 | }
81 |
82 | if ($cameraItem.Properties['pan'] -ne 'Absolute' -or $cameraItem.Properties['pan'] -ne 'Absolute' -or $cameraItem.Properties['zoom'] -ne 'Absolute') {
83 | Write-Warning "VerifyCoordinates switch provided but camera does not use absolute PTZ positioning. Coordinates will not be verified."
84 | return
85 | }
86 |
87 | $positionReached = $false
88 | $stopwatch = [Diagnostics.StopWatch]::StartNew()
89 | while ($stopwatch.ElapsedMilliseconds -lt ($timeout * 1000)) {
90 | $position = Send-MipMessage -MessageId Control.PTZGetAbsoluteRequest -DestinationEndpoint $cameraItem.FQID -UseEnvironmentManager
91 |
92 | $xDifference = [Math]::Abs($position.Pan) - [Math]::Abs($ptzPreset.Pan)
93 | $yDifference = [Math]::Abs($position.Tilt) - [Math]::Abs($ptzPreset.Tilt)
94 | $zDifference = [Math]::Abs($position.Zoom) - [Math]::Abs($ptzPreset.Zoom)
95 |
96 | if ($xDifference -gt $Tolerance) {
97 | Write-Warning "Expected Pan = $($ptzPreset.Pan), Current Pan = $($position.Pan), Off by $xDifference"
98 | }
99 | elseif ($yDifference -gt $Tolerance) {
100 | Write-Warning "Desired Tilt = $($ptzPreset.Tilt), Current Pan = $($position.Tilt), Off by $yDifference"
101 | }
102 | elseif ($zDifference -gt $Tolerance) {
103 | Write-Warning "Desired Zoom = $($ptzPreset.Zoom), Current Pan = $($position.Zoom), Off by $zDifference"
104 | }
105 | else {
106 | $positionReached = $true
107 | break
108 | }
109 | Start-Sleep -Milliseconds 100
110 | }
111 | if (-not $positionReached) {
112 | Write-Error "Camera failed to reach preset position"
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/Samples/Active-PTZ-Preset-Positions/README sp-MX.md:
--------------------------------------------------------------------------------
1 | ## Activate PTZ Preset Positions
2 | En este ejemplo, aprovecharemos una nueva característica introducida en MilestonePSTools v1.0.75, Send-MipMessage. Consulte el contenido de Invoke-PtzPreset.ps1 para obtener un ejemplo de cómo activar una posición posición prestablecida o recuperar las coordenadas PTZ actuales mediante Send-MipMessage.
3 |
4 | En la siguiente secuencia de comandos, encontraremos todas las cámaras con al menos una posición preestablecida PTZ, luego llamaremos a Invoke-PtzPreset en cada una. Luego tomaremos una instantánea de la cámara, guardando una imagen en el disco con la cámara y los nombres de posición prestablecidos en el nombre del archivo.
5 |
6 |
7 | ```powershell
8 | # Pídale a PowerShell que nos muestre mensajes de "información" que normalmente están ocultos / ignorados
9 | $InformationPreference = 'Continue'
10 |
11 | # Seleccione todas las cámaras con al menos una posición predefinida PTZ
12 | $cameras = Get-Hardware | Where-Object Enabled | Get-Camera | Where-Object { $_.Enabled -and $_.PtzPresetFolder.PtzPresets.Count -gt 0 }
13 |
14 | # Esto es "dot sourcing" donde llamamos a un script externo. En este caso, solo estamos cargando la función Invoke-PtzPreset. Asumiremos que el archivo Invoke-PtzPreset.ps1 está en la misma carpeta que este script.
15 | . .\Invoke-PtzPreset.ps1
16 |
17 | foreach ($camera in $cameras) {
18 |
19 | foreach ($ptzPreset in $camera.PtzPresetFolder.PtzPresets) {
20 |
21 | Write-Information "Moving $($camera.Name) to $($ptzPreset.Name) preset position"
22 | Invoke-PtzPreset -PtzPreset $ptzPreset -VerifyCoordinates
23 |
24 | Write-Information "Taking snapshot . . ."
25 | $snapshotParams = @{
26 | Live = $true
27 | Quality = 95
28 | Save = $true
29 | Path = "C:\demo"
30 | FileName = "$($camera.Name) -- $($ptzPreset.Name).jpg"
31 | }
32 | $null = $camera | Get-Snapshot @snapshotParams
33 | }
34 | }
35 | ```
--------------------------------------------------------------------------------
/Samples/Active-PTZ-Preset-Positions/README.md:
--------------------------------------------------------------------------------
1 | ## Activate PTZ Preset Positions
2 | In this sample we will take advantage of a new feature introduced in MilestonePSTools v1.0.75,
3 | Send-MipMessage. See the contents of Invoke-PtzPreset.ps1 for an example of how to trigger
4 | a PTZ preset position or retrieve the current PTZ coordinates using Send-MipMessage.
5 |
6 | In the following script, we'll find all cameras with at least one PTZ preset position, then
7 | call Invoke-PtzPreset on each one. Then we'll take a snapshot of the camera, saving an image
8 | to disk with the camera and preset position names in the file name.
9 |
10 | ```powershell
11 | # Ask PowerShell to show us "Information" messages which are normally hidden/ignored
12 | $InformationPreference = 'Continue'
13 |
14 | # Select all cameras with at least one PTZ preset position
15 | $cameras = Get-Hardware | Where-Object Enabled | Get-Camera | Where-Object { $_.Enabled -and $_.PtzPresetFolder.PtzPresets.Count -gt 0 }
16 |
17 | # This is "dot sourcing" where we call an external script. In this case we're just
18 | # loading the Invoke-PtzPreset function. We'll assume the Invoke-PtzPreset.ps1 file
19 | # is in the same folder as this script.
20 | . .\Invoke-PtzPreset.ps1
21 |
22 | foreach ($camera in $cameras) {
23 |
24 | foreach ($ptzPreset in $camera.PtzPresetFolder.PtzPresets) {
25 |
26 | Write-Information "Moving $($camera.Name) to $($ptzPreset.Name) preset position"
27 | Invoke-PtzPreset -PtzPreset $ptzPreset -VerifyCoordinates
28 |
29 | Write-Information "Taking snapshot . . ."
30 | $snapshotParams = @{
31 | Live = $true
32 | Quality = 95
33 | Save = $true
34 | Path = "C:\demo"
35 | FileName = "$($camera.Name) -- $($ptzPreset.Name).jpg"
36 | }
37 | $null = $camera | Get-Snapshot @snapshotParams
38 | }
39 | }
40 | ```
--------------------------------------------------------------------------------
/Samples/AddRemoveViewLayouts/Remove-VmsViewLayout.ps1:
--------------------------------------------------------------------------------
1 | function Remove-VmsViewLayout {
2 | <#
3 | .SYNOPSIS
4 | Removes a view layout, that has previously been added, from the Milestone XProtect system
5 | .DESCRIPTION
6 | Removes a custom view layout that was added via Add-VmsViewLayout or some other method. The view layout name and the
7 | layout group need to be provided.
8 | .PARAMETER ViewLayoutName
9 | Specify the name of the view to be removed.
10 | .PARAMETER LayoutFolder
11 | Specify which view layout group the view to be removed resides in.
12 | .PARAMETER ListCustomLayouts
13 | List all custom layouts along with the Layout Folder they belong to.
14 | .EXAMPLE
15 | Remove-VmsViewLayout -ViewLayoutName 'Sample View' -LayoutFolder '16:9'
16 |
17 | Removes custom view layout named 'Sample View'
18 | .EXAMPLE
19 | # Connect-Vms only required if not already connected
20 | Connect-Vms -ShowDialog -AcceptEula
21 | Remove-VmsViewLayout -ListCustomLayouts
22 |
23 | Returns a list of all custom layouts and which Layout Folder they belong to
24 | .NOTES
25 | The software provided by Milestone Systems, Inc. (hereinafter referred to as "the Software") is provided on
26 | an "as is" basis, without any warranties or representations, express or implied, including but not limited to
27 | the implied warranties of merchantability, fitness for a particular purpose, or non-infringement.
28 |
29 | Warranty Disclaimer:
30 | The Software is provided without any warranty of any kind, whether expressed or implied. Milestone Systems, Inc.
31 | expressly disclaims all warranties, conditions, and representations, including but not limited to warranties of
32 | title, non-infringement, merchantability, or fitness for a particular purpose. The entire risk arising out of the
33 | use or performance of the Software remains with the user.
34 |
35 | Support Disclaimer:
36 | Milestone Systems, Inc. does not provide any support or maintenance services for the Software. The user acknowledges
37 | and agrees that Milestone Systems, Inc. shall have no obligation to provide any updates, bug fixes, or technical
38 | support for the Software, whether through telephone, email, or any other means.
39 |
40 | User Responsibility:
41 | The user acknowledges and agrees that they are solely responsible for the selection, installation, use, and results
42 | obtained from the Software. Milestone Systems, Inc. shall not be held liable for any errors, defects, or damages arising
43 | from the use or inability to use the Software, including but not limited to direct, indirect, incidental, consequential,
44 | or special damages.
45 |
46 | Indemnification:
47 | The user agrees to indemnify, defend, and hold harmless Milestone Systems, Inc. and its directors, officers, employees,
48 | and agents from any and all claims, liabilities, damages, losses, costs, and expenses (including reasonable attorneys' fees)
49 | arising out of or related to the user's use or misuse of the Software.
50 |
51 | By using the Software, the user acknowledges that they have read and understood this clause and agree to be bound by its terms.
52 | #>
53 |
54 | [CmdletBinding()]
55 | param (
56 | [Parameter(Mandatory, ParameterSetName = 'Remove')]
57 | [string]
58 | $ViewLayoutName,
59 | [Parameter(Mandatory, ParameterSetName = 'Remove')]
60 | [ValidateSet('4:3','16:9','4:3 Portrait','16:9 Portrait')]
61 | [string]
62 | $LayoutFolder,
63 | [Parameter(Mandatory, ParameterSetName = 'List')]
64 | [switch]
65 | $ListCustomLayouts
66 | )
67 |
68 | $ms = Get-VmsManagementServer -ErrorAction SilentlyContinue
69 | if ([string]::IsNullOrEmpty($ms.Version)) {
70 | Write-Warning "Please connect to a Milestone XProtect system first."
71 | break
72 | }
73 |
74 | $layoutGroups = $ms.LayoutGroupFolder.LayoutGroups
75 | $customViews = New-Object System.Collections.Generic.List[PSCustomObject]
76 | if ($ListCustomLayouts) {
77 | foreach ($lg in $layoutGroups) {
78 | $task = $lg.LayoutFolder.RemoveLayout()
79 | ($task.ItemSelectionValues).Keys | ForEach-Object {
80 | $viewName = $_
81 | $row = [PSCustomObject]@{
82 | "View Layout Name" = $viewName
83 | "View Layout Folder" = $lg.Name
84 | }
85 | $customViews.Add($row)
86 | }
87 | }
88 |
89 | if (-not [string]::IsNullOrEmpty($customViews.'View Layout Folder')) {
90 | return $customViews
91 | break
92 | } else {
93 | Write-Warning "There are no custom view layouts in this system."
94 | break
95 | }
96 | }
97 |
98 | $layoutGroup = $layoutGroups | Where-Object {$_.Name -eq $LayoutFolder}
99 | $layout = $layoutGroup.LayoutFolder.Layouts | Where-Object {$_.Name -eq $ViewLayoutName}
100 | if ([string]::IsNullOrEmpty($layout.Id)) {
101 | Write-Warning 'The selected view does not exist in the selected view layout group.'
102 | break
103 | }
104 |
105 | $null = $layoutGroup.LayoutFolder.RemoveLayout($layout.Path)
106 | }
--------------------------------------------------------------------------------
/Samples/Add_Hardware_from_CSV.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Add hardware from a CSV file
3 |
4 | This sample shows how you can use Import-HardwareCsv to add and configure
5 | many cameras quickly.
6 |
7 | To use the sample, please login to your VMS with Connect-ManagementServer
8 | and consider changing which Recording Server the camera is added to. The
9 | first Recording Server returned by Get-RecordingServer will be used.
10 |
11 | The script below will first create a CSV file with a universal driver
12 | camera to use in the Import-HardwareCsv command. You would normally prepare
13 | your own CSV file instead. The CSV generated by this script will be placed
14 | in the current folder and will look like this:
15 |
16 | "HardwareName","HardwareAddress","UserName","Password","DriverNumber","GroupPath"
17 | "Universal Driver","http://wowzaec2demo.streamlock.net","root","pass","421","/New Cameras"
18 | #>
19 |
20 | $rows = @([pscustomobject]@{
21 | HardwareName = "Universal Driver"
22 | HardwareAddress = "http://wowzaec2demo.streamlock.net"
23 | UserName = 'root'
24 | Password = 'pass'
25 | DriverNumber = 421
26 | GroupPath = "/New Cameras"
27 | })
28 | $rows | Export-Csv .\test.csv -NoTypeInformation
29 |
30 | $recorder = (Get-RecordingServer)[0]
31 | $newHardware = Import-HardwareCsv -Path .\test.csv -RecordingServer $recorder
32 |
33 | foreach ($hardware in $newHardware) {
34 | [pscustomobject]@{
35 | Name = $hardware.Name
36 | Id = $hardware.Id
37 | Address = $hardware.Address
38 | Cameras = $hardware.CameraFolder.Cameras.Count
39 | }
40 | }
--------------------------------------------------------------------------------
/Samples/Adding_Hardware_with_Universal_Driver.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Add hardware using the Universal Driver
3 |
4 | This sample shows how you can add and configure an RTSP stream.
5 | The RTSP stream in this sample is hosted by Wowza Streaming Engine at
6 | https://www.wowza.com/html/mobile.html
7 |
8 | To use the sample, please login to your VMS with Connect-ManagementServer
9 | and consider changing which Recording Server the camera is added to. It
10 | will be added to the first Recording Server returned by Get-RecordingServer
11 | #>
12 |
13 | # Retrieve the first Recording Server
14 | $recorder = Get-RecordingServer | Select-Object -First 1
15 | $hardwareParams = @{
16 | Address = 'http://wowzaec2demo.streamlock.net'
17 | DriverId = 421
18 | GroupPath = '/Add-Hardware Demo'
19 | }
20 |
21 | try {
22 | $hardware = $recorder | Add-Hardware @hardwareParams
23 | }
24 | catch {
25 | # If the hardware fails to add for some reason, lets quit the script
26 | throw
27 | }
28 |
29 | # Select the camera device at index 0 on the new hardware and configure it
30 | $camera = $hardware | Get-Camera -Channel 0
31 | $camera | Set-CameraSetting -Stream -StreamNumber 0 -Name FPS -Value 25
32 | $camera | Set-CameraSetting -Stream -StreamNumber 0 -Name StreamingMode -Value 'RTP over RTSP (TCP)'
33 | $camera | Set-CameraSetting -Stream -StreamNumber 0 -Name ConnectionURI -Value 'vod/mp4:BigBuckBunny_115k.mov'
34 |
35 | # Select the microphone, configure it, and enable it
36 | $microphone = $hardware | Get-Microphone -Channel 0
37 | $microphone | Set-MicrophoneSetting -Stream -StreamNumber 0 -Name Codec -Value AAC
38 | $microphone | Set-MicrophoneSetting -Stream -StreamNumber 0 -Name ConnectionURI -Value 'vod/mp4:BigBuckBunny_115k.mov'
39 | $microphone | Set-MicrophoneSetting -Stream -StreamNumber 0 -Name StreamingMode -Value 'RTP over RTSP (TCP)'
40 | $microphone.Enabled = $true; $microphone.Save()
41 | # Create a device group for the microphone and add the mic to it
42 | $group = Add-DeviceGroup -DeviceCategory Microphone -Path '/New Mics'
43 | Add-DeviceGroupMember -DeviceGroup $group -DeviceCategory Microphone -DeviceId $microphone.Id
44 |
45 | # Lets just show some information about the newly added device at the end
46 | [pscustomobject]@{
47 | Camera = $camera.Name
48 | Id = $camera.Id
49 | Uri = ($camera | Get-CameraSetting -Stream -StreamNumber 0).ConnectionURI
50 | }
--------------------------------------------------------------------------------
/Samples/BackupMediaDb/BackupMediaDb.psm1:
--------------------------------------------------------------------------------
1 | $public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -Recurse -ErrorAction Ignore )
2 | $private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -Recurse -ErrorAction Ignore )
3 |
4 | foreach ($import in $public + $private) {
5 | try {
6 | . $import.FullName
7 | }
8 | catch {
9 | Write-Error "Failed to import function $($import.FullName): $_"
10 | }
11 | }
12 |
13 | Export-ModuleMember -Function $public.BaseName
--------------------------------------------------------------------------------
/Samples/BackupMediaDb/Public/Backup-Bank.ps1:
--------------------------------------------------------------------------------
1 | function Backup-Bank {
2 | [CmdletBinding()]
3 | param (
4 | [Parameter(Mandatory)]
5 | [ValidateScript({Test-Path $_})]
6 | [string]
7 | $BankPath,
8 | [Parameter(Mandatory)]
9 | [ValidateScript({Test-Path $_})]
10 | [string]
11 | $Destination,
12 | [Parameter()]
13 | [DateTime]
14 | $Start = [DateTime]::Now.AddYears(-100),
15 | [Parameter()]
16 | [DateTime]
17 | $End = [DateTime]::Now,
18 | [Parameter()]
19 | [string[]]
20 | $DeviceId
21 | )
22 |
23 | begin {
24 | }
25 |
26 | process {
27 | $bankId = Split-Path -Path $BankPath -Leaf
28 | $backupFolder = Join-Path $Destination $bankId
29 | if (-not (Test-Path $backupFolder)) {
30 | $null = New-Item -Path $backupFolder -ItemType Directory -Force
31 | }
32 |
33 | Get-ChildItem -Path (Join-Path -Path $BankPath "*.xml") | Copy-Item -Destination $backupFolder
34 |
35 | foreach ($table in Get-BankTable -Path $BankPath -StartTime $Start -EndTime $End -DeviceId $DeviceId) {
36 | $source = $table.Path
37 | $dest = Join-Path $backupFolder (Split-Path $table.Path -Leaf)
38 | $log = Join-Path $backupFolder "robocopy.log"
39 | robocopy $source $dest /E /Z /NP /MT:8 /LOG+:$log
40 | }
41 | }
42 |
43 | end {
44 |
45 | }
46 | }
--------------------------------------------------------------------------------
/Samples/BackupMediaDb/Public/Backup-MediaDb.ps1:
--------------------------------------------------------------------------------
1 | function Backup-MediaDb {
2 | [CmdletBinding()]
3 | param (
4 | [Parameter(Mandatory)]
5 | [ValidateScript({Test-Path $_})]
6 | [string]
7 | $Destination,
8 | [Parameter()]
9 | [DateTime]
10 | $Start = [DateTime]::Now.AddYears(-100),
11 | [Parameter()]
12 | [DateTime]
13 | $End = [DateTime]::Now,
14 | [Parameter()]
15 | [string[]]
16 | $DeviceId
17 | )
18 |
19 | begin {
20 | $recorderConfig = Get-RecorderConfig
21 | if ($null -eq $recorderConfig) {
22 | throw "Get-RecorderConfig failed to return RecorderConfig information"
23 | }
24 | }
25 |
26 | process {
27 | $backupFolder = Join-Path $Destination $recorderConfig.RecorderId
28 | if (-not (Test-Path $backupFolder)) {
29 | $null = New-Item -Path $backupFolder -ItemType Directory -Force
30 | }
31 |
32 | $recorder = Get-RecordingServer -Id $recorderConfig.RecorderId
33 | foreach ($storage in $recorder.StorageFolder.Storages) {
34 | Backup-Storage -Storage $storage -Destination $backupFolder -Start $Start -End $End -DeviceId $DeviceId
35 | }
36 | }
37 |
38 | end {
39 |
40 | }
41 | }
--------------------------------------------------------------------------------
/Samples/BackupMediaDb/Public/Backup-Storage.ps1:
--------------------------------------------------------------------------------
1 | function Backup-Storage {
2 | [CmdletBinding()]
3 | param (
4 | [Parameter(Mandatory)]
5 | [ValidateNotNull()]
6 | [VideoOS.Platform.ConfigurationItems.Storage]
7 | $Storage,
8 | [Parameter(Mandatory)]
9 | [ValidateScript({Test-Path $_})]
10 | [string]
11 | $Destination,
12 | [Parameter()]
13 | [DateTime]
14 | $Start = [DateTime]::Now.AddYears(-100),
15 | [Parameter()]
16 | [DateTime]
17 | $End = [DateTime]::Now,
18 | [Parameter()]
19 | [string[]]
20 | $DeviceId
21 | )
22 |
23 | begin {
24 | }
25 |
26 | process {
27 | $Storage | ConvertTo-Json | Out-File -FilePath (Join-Path $Destination "$($Storage.Id).json")
28 | $backupFolder = Join-Path $Destination $Storage.Id
29 | if (-not (Test-Path $backupFolder)) {
30 | $null = New-Item -Path $backupFolder -ItemType Directory -Force
31 | }
32 |
33 | Backup-Bank -BankPath (Join-Path $Storage.DiskPath $Storage.Id) -Destination $backupFolder -Start $Start -End $End -DeviceId $DeviceId
34 | foreach ($archive in $Storage.ArchiveStorageFolder.ArchiveStorages | Sort-Object RetainMinutes) {
35 | Backup-Bank -BankPath (Join-Path $archive.DiskPath $archive.Id) -Destination $backupFolder -Start $Start -End $End -DeviceId $DeviceId
36 | }
37 | }
38 |
39 | end {
40 |
41 | }
42 | }
--------------------------------------------------------------------------------
/Samples/BackupMediaDb/README sp-MX.md:
--------------------------------------------------------------------------------
1 | ## BackupMediaDb
2 | En este ejemplo se muestra cómo puede usar una combinación de características de PowerShell, MilestonePSTools y robocopy para realizar copias de seguridad de parte de una base de datos multimedia de Milestone en un servidor de grabación.
3 |
4 | El cmdlet Backup-MediaDb está diseñado para ejecutarse directamente en un servidor de grabación después de conectarse al servidor de gestión con el cmdlet Connect-ManagementServer MilestonePSTools. El identificador del servidor de grabación se detectará automáticamente mediante el cmdlet Get-RecorderConfig y la configuración de almacenamiento de la grabadora se leerá desde el servidor de gestión. A continuación, se comprobará cada configuración de almacenamiento en busca de tablas que coincidan con los criterios de tiempo e identificación de dispositivo proporcionados.
5 |
6 |
7 | ```powershell
8 | # Backup the last 7 days worth of files from any and all Media Database
9 | # locations on this Recording Server
10 | Connect-ManagementServer
11 | Backup-MediaDb -Destination E:\backup -Start (Get-Date).AddDays(-7) -End (Get-Date)
12 | ```
--------------------------------------------------------------------------------
/Samples/BackupMediaDb/README.md:
--------------------------------------------------------------------------------
1 | ## BackupMediaDb
2 | This sample demonstrates how you could use a mix of features from PowerShell,
3 | MilestonePSTools, and robocopy to backup part of a Milestone media database
4 | on a Recording Server.
5 |
6 | The Backup-MediaDb cmdlet is intended to be run directly on a Recording Server
7 | after connecting to the Management Server with the Connect-ManagementServer
8 | MilestonePSTools cmdlet. The Recording Server ID will be autodetected using the
9 | Get-RecorderConfig cmdlet, and the storage configuration for the recorder will
10 | be read from the Management Server. Then each storage configuration will be
11 | checked for tables matching the provided time and device ID criteria.
12 |
13 | ```powershell
14 | # Backup the last 7 days worth of files from any and all Media Database
15 | # locations on this Recording Server
16 | Connect-ManagementServer
17 | Backup-MediaDb -Destination E:\backup -Start (Get-Date).AddDays(-7) -End (Get-Date)
18 | ```
--------------------------------------------------------------------------------
/Samples/Daily-LPR-Exports/SetupDailyLPRExports.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 |
4 | Creates a scheduled task in Windows to export all LPR events from the past 7 days to a CSV file.
5 |
6 | .DESCRIPTION
7 |
8 | This script will prompt the user for a path to store the server address and credentials in a
9 | configuration file, followed by the VMS server address, credentials, and whether the credential is
10 | for a basic user. These settings will be stored in the given file path as configuration.xml after
11 | we have verified we can login to the VMS using the given credentials.
12 |
13 | A scheduled task will then be created to run once every day at 7am. That task will login to the
14 | VMS, retrieve all 'LPR Event' events for the last 7 days, and export them to a csv file in the
15 | provided path. A log file will also be created which is useful for troubleshooting.
16 |
17 | This script is designed to be run again if you want to change something. If the scheduled task
18 | already exists, it will be removed before creating a new one.
19 |
20 | #>
21 |
22 | #Requires -RunAsAdministrator
23 | #Requires -Modules MilestonePSTools
24 | $InformationPreference = "Continue"
25 | $ErrorActionPreference = "Stop"
26 |
27 | $path = Read-Host -Prompt "Path to store configuration and reports"
28 | if (-not (Test-Path $path)) {
29 | $null = mkdir $path
30 | }
31 |
32 | do {
33 | try {
34 | $serverAddress = Read-Host -Prompt "Milestone VMS Server Address"
35 | $credential = Get-Credential -Message "Milestone VMS Credentials"
36 | $basicUser = (Read-Host -Prompt "Basic User? (y/n)") -eq 'y'
37 | Write-Information "Testing VMS server connection. . ."
38 | Connect-ManagementServer -Server $serverAddress -Credential $credential -BasicUser:$basicUser
39 | Write-Information "VMS connection successful. Disconnecting. . ."
40 | Disconnect-ManagementServer
41 | Write-Information "Disconnected"
42 | break
43 | }
44 | catch {
45 | Write-Warning "Connection to the VMS failed. Please re-enter the information."
46 | }
47 | } while ($true)
48 |
49 |
50 | # Gather the necessary variables and save them to a file. The credential is stored in an encrypted format
51 | $configuration = [pscustomobject]@{
52 | Server = $serverAddress
53 | Credential = $credential
54 | BasicUser = $basicUser
55 | }
56 | $configuration | Export-Clixml -Path (Join-Path -Path $path configuration.xml)
57 |
58 | # Create our Scheduled Task trigger to run daily at 7am. Modify the 'At' parameter to change the time of day the task will run
59 | $jobTrigger = New-JobTrigger -Daily -At (Get-Date -Hour 7)
60 |
61 | # Find and delete this job if it already exists so we can modify and recreate the job using the same script
62 | Get-ScheduledJob -Name 'Daily Milestone LPR Export' -ErrorAction SilentlyContinue | Unregister-ScheduledJob
63 | Write-Information "Creating scheduled task. . ."
64 | $job = Register-ScheduledJob -Name 'Daily Milestone LPR Export' -Trigger $jobTrigger -ArgumentList $path -ScriptBlock {
65 | param($path)
66 | $InformationPreference = "Continue"
67 | Start-Transcript -Path (Join-Path $path 'daily-lpr-export.log')
68 | $configuration = Import-Clixml -Path (Join-Path $path configuration.xml)
69 | $connected = $false
70 | try {
71 | Write-Host "$(Get-Date) - Connecting to $($configuration.Server) with user $($configuration.Credential.UserName). . ."
72 | Connect-ManagementServer -Server $configuration.Server -Credential $configuration.Credential -BasicUser:$configuration.BasicUser
73 | Write-Host "$(Get-Date) - Connected"
74 | $connected = $true
75 | $timeCondition = New-AlarmCondition -Target Timestamp -Operator GreaterThan -Value (Get-Date).AddDays(-7).ToUniversalTime()
76 | $typeCondition = New-AlarmCondition -Target Type -Operator Equals -Value 'LPR Event'
77 | Write-Host "$(Get-Date) - Retrieving LPR Events. . ."
78 | Get-EventLine -Conditions $timeCondition, $typeCondition -PageSize 1000 -Verbose | `
79 | Select Timestamp, SourceName, ObjectValue | `
80 | Export-Csv -Path (Join-Path $path lpr_export.csv) -NoTypeInformation
81 | Write-Host "$(Get-Date) - LPR data exported to $(Join-Path $path lpr_export.csv)"
82 | }
83 | finally {
84 | if ($connected) {
85 | Write-Host "$(Get-Date) - Disconnecting from $($configuration.Server)"
86 | Disconnect-ManagementServer
87 | }
88 | Stop-Transcript
89 | }
90 | }
91 | Write-Information "Scheduled Task $($job.Name) created with ID $($job.Id). It will now run daily at $($jobTrigger.At.Hour)"
92 | if ((Read-Host -Prompt "Would you like to test this scheduled task now? (y/n)") -eq 'y') {
93 | Write-Information "Running the scheduled task - the log will be displayed when completed. . ."
94 | $job.Run()
95 | Get-Content -Path (Join-Path $path 'daily-lpr-export.log')
96 | }
--------------------------------------------------------------------------------
/Samples/Extend_Get-CameraReport.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Extend Get-CameraReport by performing a Test-NetConnection against any
3 | cameras with the state 'Not Responding' in the VMS.
4 |
5 | To use the sample, please login to your VMS with Connect-ManagementServer
6 | and ensure you are running the script from a PC with direct network access
7 | to the cameras. If the cameras are segmented from the rest of the network
8 | you may need to run this script on your Recording Server(s) or Management
9 | Server.
10 |
11 | Finally, the results in this script are piped to Out-GridView. You may wish
12 | to send the results to Export-Csv instead.
13 | #>
14 |
15 | $report = Get-CameraReport
16 |
17 | foreach ($row in $report) {
18 | $networkState = 'Online'
19 |
20 | if ($row.State -eq 'Not Responding') {
21 | $uri = [Uri]$row.Address
22 | $reachable = Test-NetConnection -ComputerName $uri.Host -Port $uri.Port -InformationLevel Quiet
23 | if (-not $reachable) {
24 | $networkState = 'Offline'
25 | }
26 | }
27 |
28 | $row | Add-Member -MemberType NoteProperty -Name 'NetworkState' -Value $networkState
29 | }
30 |
31 | $report | Out-GridView
--------------------------------------------------------------------------------
/Samples/Find-XProtectDevice.ps1:
--------------------------------------------------------------------------------
1 | function Find-XProtectDevice {
2 | <#
3 | .SYNOPSIS
4 | Search for any string of text related to hardware or any device attached to hardware.
5 | .DESCRIPTION
6 | Search the properties for any string related to hardware or cameras, microphones, speakers, metadata channels, inputs, or outputs attached to hardware. All results will return the Recording Server and Hardware that the device is attached to.
7 | .EXAMPLE
8 | Find-XProtectDevice -ItemType Hardware -SearchString "10.1.10.30" -EnabledOnly
9 |
10 | Searches for any Enabled hardware with "10.1.10.30" existing on any of its properties
11 | .EXAMPLE
12 | Find-XProtectDevice -ItemType Camera -SearchString "Northwest"
13 |
14 | Searches for any camera (enabled or disabled) with "Northwest" existing on any of its properties
15 | .EXAMPLE
16 | Find-XProtectDevice -ItemType Speaker -SearchString parking*entrance -EnabledOnly
17 |
18 | Searches for any Enabled speaker that has the words "parking" and "entrance" (in that order)
19 | .EXAMPLE
20 | Find-XProtectDevice -ItemType Hardware -SearchString dd*be
21 |
22 | When searching for a MAC address, seperate each octet with an asterisk (*) because some MAC addresses are stored with the separating colons and some are stored without the separating colons. By using the asterisk, it will catch it either way.
23 | #>
24 | [CmdletBinding()]
25 | param (
26 | [Parameter(Mandatory)]
27 | [string]
28 | [ValidateSet('Hardware','Camera','Microphone','Speaker','Metadata','Input','Output')]
29 | $ItemType,
30 | [Parameter(Mandatory)]
31 | [string]
32 | $SearchString,
33 | [Parameter()]
34 | [switch]
35 | $EnabledOnly
36 | )
37 | process {
38 | $recs = Get-RecordingServer
39 |
40 | if ($EnabledOnly -eq $true)
41 | {
42 | $hw = Get-Hardware | Where-Object Enabled
43 | switch ($ItemType) {
44 | 'Hardware' {$items = $hw | Where-Object Enabled}
45 | 'Camera' {$items = $hw | Get-Camera | Where-Object Enabled}
46 | 'Microphone' {$items = $hw | Get-Microphone | Where-Object Enabled}
47 | 'Speaker' {$items = $hw | Get-Speaker | Where-Object Enabled}
48 | 'Metadata' {$items = $hw | Get-Metadata | Where-Object Enabled}
49 | 'Input' {$items = $hw | Get-Input | Where-Object Enabled}
50 | 'Output' {$items = $hw | Get-Output | Where-Object Enabled}
51 | }
52 | } else {
53 | $hw = Get-Hardware
54 | switch ($ItemType) {
55 | 'Hardware' {$items = $hw}
56 | 'Camera' {$items = $hw | Get-Camera}
57 | 'Microphone' {$items = $hw | Get-Microphone}
58 | 'Speaker' {$items = $hw | Get-Speaker}
59 | 'Metadata' {$items = $hw | Get-Metadata}
60 | 'Input' {$items = $hw | Get-Input}
61 | 'Output' {$items = $hw | Get-Output}
62 | }
63 | }
64 |
65 | $found = New-Object System.Collections.Generic.List[PSCustomObject]
66 |
67 | if ($ItemType -ne 'Hardware')
68 | {
69 | foreach ($device in $items)
70 | {
71 | $deviceHash = Convert-ObjectToHashTable $device
72 | $deviceHash.GetEnumerator() | ForEach-Object {
73 | if ($null -ne $_.Value -and $_.Value -like "*$($SearchString)*")
74 | {
75 | $hwObject = $hw | Where-Object {$device.ParentItemPath -eq $_.Path}
76 |
77 | $hwName = $hwObject.Name
78 | $recObject = $recs | Where-Object {$hwObject.ParentItemPath -eq $_.Path}
79 | $recName = $recObject.Name
80 |
81 | $row = [PSCustomObject]@{
82 | 'RecordingServer' = $recName
83 | 'HardwareName' = $hwName
84 | 'DeviceName' = $device
85 | }
86 | $found.Add($row)
87 | }
88 | }
89 | }
90 | $foundSorted = $found | Sort-Object RecordingServer, HardwareName, DeviceName -Unique
91 | } else
92 | {
93 | foreach ($hardware in $items)
94 | {
95 | $hardwareSettings = $hardware | Get-HardwareSetting
96 |
97 | $hardwareHash = Convert-ObjectToHashTable $hardware
98 | $hardwareSettingsHash = Convert-ObjectToHashTable $hardwareSettings
99 | $allHardwareInfoHash = $hardwareHash + $hardwareSettingsHash
100 | $allHardwareInfoHash.GetEnumerator() | ForEach-Object {
101 | if ($null -ne $_.Value -and $_.Value -like "*$($SearchString)*")
102 | {
103 | $recObject = $recs | Where-Object {$hardware.ParentItemPath -eq $_.Path}
104 | $recName = $recObject.Name
105 |
106 | $row = [PSCustomObject]@{
107 | 'RecordingServer' = $recName
108 | 'HardwareName' = $hardware.Name
109 | }
110 | $found.Add($row)
111 | }
112 | }
113 | }
114 | $foundSorted = $found | Sort-Object RecordingServer, HardwareName -Unique
115 | }
116 | }
117 | end {
118 | if ($null -ne $foundSorted)
119 | {
120 | $foundSorted
121 | } else {
122 | Write-Host "No results found!" -ForegroundColor Green
123 | }
124 | }
125 | }
126 |
127 | function Convert-ObjectToHashTable
128 | {
129 | <#
130 | .SYNOPSIS
131 | Converts an Object to a Hash Table
132 | #>
133 | [CmdletBinding()]
134 | param
135 | (
136 | [parameter(Mandatory=$true,ValueFromPipeline=$true)]
137 | [pscustomobject] $Object
138 | )
139 | $HashTable = @{}
140 | $ObjectMembers = Get-Member -InputObject $Object -MemberType *Property
141 | foreach ($Member in $ObjectMembers)
142 | {
143 | $HashTable.$($Member.Name) = $Object.$($Member.Name)
144 | }
145 | return $HashTable
146 | }
--------------------------------------------------------------------------------
/Samples/General_Login_Script.ps1:
--------------------------------------------------------------------------------
1 | ###################################################################################################
2 | #
3 | # This login script can be placed in front of any other script or used standalone to simplify the
4 | # login process for a user that isn't as familiar with PowerShell as other users. It will prompt
5 | # for the IP address or hostname of the Management Server, then it will ask what user type,
6 | # and then it asks for the credentials and completes the login process.
7 | #
8 | ###################################################################################################
9 |
10 | Import-Module MilestonePSTools
11 | $currentErrorActionPreference = $ErrorActionPreference
12 | $ErrorActionPreference = "Stop"
13 |
14 | # Checks to see if the MIP SDK EULA has been accepted already. If it hasn't, it asks the user to accept it.
15 | if ((Test-Path "$($env:USERPROFILE)\AppData\Roaming\MilestonePSTools\user-accepted-eula.txt") -ne $true)
16 | {
17 | do
18 | {
19 | Write-Host "Have you read and agree to the End User License Agreement for the Milestone Redistributable MIP SDK? (Y or N): " -NoNewline -ForegroundColor Green
20 | $eulaAcceptance = Read-Host
21 | Switch ($eulaAcceptance)
22 | {
23 | "Y" {Continue}
24 | "N" {
25 | Invoke-MipSdkEula
26 | }
27 | Default
28 | {
29 | Write-Host "Invalid input. Please try again." -ForegroundColor White -BackgroundColor Red -NoNewline
30 | }
31 | }
32 | } while ($eulaAcceptance -ne "Y")
33 | }
34 |
35 | Write-Host 'Please enter IP Address or hostname of the Management Server (leave blank for "localhost"): ' -NoNewline -ForegroundColor Green
36 | $server = Read-Host
37 |
38 | # If no server name was entered then set $server to localhost
39 | if ([string]::IsNullOrWhiteSpace($server))
40 | {
41 | $server = "localhost"
42 | }
43 |
44 | # Sets $userType to the appropriate user type based on the input of 1, 2, or 3 requested.
45 | do
46 | {
47 | $userType = ""
48 | Write-Host "`nPlease enter your user type (without quotes). Enter `"1`" for Windows Authentication (current user), enter `"2`" for Windows Authentication, or enter `"3`" for Basic User: " -NoNewline -ForegroundColor Green
49 | $auth = Read-Host
50 | Switch ($auth)
51 | {
52 | 1 {$userType = "CurrentUser"; break}
53 | 2 {$userType = "WindowsUser"; break}
54 | 3 {$userType = "BasicUser"; break}
55 | Default
56 | {
57 | Write-Host "Invalid input. Please try again." -ForegroundColor White -BackgroundColor Red -NoNewline
58 | }
59 | }
60 | } while ((($auth -ne 1) -and ($auth -ne 2) -and ($auth -ne 3)))
61 |
62 | Write-Host "`nConnecting to Management Server.`n" -ForegroundColor Green
63 |
64 | try
65 | {
66 | # Start the login process. If $userType is WindowsUser or BasicUser, it will prompt for credentials.
67 | if ($userType -eq "WindowsUser" -or $userType -eq "BasicUser")
68 | {
69 | switch ($userType)
70 | {
71 | "WindowsUser" {Connect-ManagementServer -Server $server -Credential (Get-Credential) -AcceptEula -Force}
72 | "BasicUser" {Connect-ManagementServer -Server $server -Credential (Get-Credential) -AcceptEula -BasicUser -Force}
73 | }
74 | }
75 | else
76 | {
77 | Connect-ManagementServer -Server $server -AcceptEula
78 | }
79 | }
80 |
81 | catch
82 | {
83 | $categoryError = $_.CategoryInfo
84 | switch ($categoryError.Reason)
85 | {
86 | "ServerNotFoundMIPException" {Write-Host "Server $($server) not found. Please try again." -ForegroundColor Red}
87 | "InvalidCredentialsMIPException" {Write-Host "Authentication error. Please try again." -ForegroundColor Red}
88 | }
89 | Exit
90 | }
91 |
92 | finally
93 | {
94 | $ErrorActionPreference = $currentErrorActionPreference
95 | }
96 |
97 | $ms = Get-VmsManagementServer -ErrorAction Ignore
98 | if ($null -ne $ms)
99 | {
100 | Write-Host "`nSuccessfully connected to Management Server" -ForegroundColor Green
101 | }
--------------------------------------------------------------------------------
/Samples/Get-CameraConnectivityReport.ps1:
--------------------------------------------------------------------------------
1 | function Get-CameraConnectivityReport {
2 | <#
3 | .SYNOPSIS
4 | Gets report of number of times cameras were offline over specified period and if the camera is still offline.
5 | .DESCRIPTION
6 | Gets report of number of times cameras were offline over specified period and if the camera is still offline.
7 | .EXAMPLE
8 | Connect-ManagementServer -ShowDialog -Force
9 | Get-CameraConnectivityReport -DurationDays 3
10 |
11 | Returns a report of number of times cameras were offline over the last 3 days and if they are still offline
12 | #>
13 |
14 | [CmdletBinding()]
15 | param (
16 | [Parameter(Mandatory=$true)]
17 | [int]
18 | $DurationDays
19 | )
20 |
21 | $camStatus = New-Object System.Collections.Generic.List[PSCustomObject]
22 | $commErrors = Get-VmsLog -LogType System -StartTime (Get-Date).AddDays(-$DurationDays) -EndTime (Get-Date) | Where-Object {$_.'Message text' -eq "Communication error." -and $_.'Source type' -eq "Device"}
23 | $groupedCommErrors = $commErrors.'Source name' | Group-Object
24 |
25 | foreach($rec in Get-VmsRecordingServer)
26 | {
27 | $deviceStatus = Get-VmsDeviceStatus -RecordingServerId $rec.Id -DeviceType Camera
28 | foreach($hw in $rec | Get-VmsHardware | Where-Object Enabled)
29 | {
30 | foreach($cam in $hw | Get-VmsCamera | Where-Object Enabled)
31 | {
32 | if(($groupedCommErrors | Where-Object {$_.Name -eq $cam.Name}).Count -gt 0)
33 | {
34 | $offlineCount = ($groupedCommErrors | Where-Object {$_.Name -eq $cam.Name}).Count
35 | } else {
36 | $offlineCount = 0
37 | }
38 |
39 | $row = [PSCustomObject]@{
40 | RecordingServer = $rec.Name
41 | Hardware = $hw.Name
42 | Camera = $cam.Name
43 | NumberOfTimesOffline = $offlineCount
44 | "StillOffline?" = ($deviceStatus | Where-Object DeviceName -eq $cam.Name).Error
45 | }
46 | $camStatus.Add($row)
47 | }
48 | }
49 | }
50 | $camStatus | Out-GridView
51 | }
--------------------------------------------------------------------------------
/Samples/Get-CameraGroupDeviceCount.ps1:
--------------------------------------------------------------------------------
1 | function Get-CameraGroupDeviceCount
2 | {
3 | <#
4 | .SYNOPSIS
5 | Calculates and displays the number of cameras in each camera group.
6 |
7 | .DESCRIPTION
8 | Will provide an output displaying the cumulative number of cameras in each group (including cameras in subgroups)
9 | as well as just cameras in that particular group (not including subgroups). The -IncludeCameraNames switch can also
10 | be used to display the cameras that are in each group.
11 |
12 | .PARAMETER IncludeCameraNames
13 | Specifies the hostname of the Recording Server the script should be run against. The hostname must
14 | exactly match the hostname shown in the Management Client.
15 |
16 | .EXAMPLE
17 | Get-CameraGroupDeviceCount
18 |
19 | Will create a tree report showing just the number of cameras in each camera group.
20 |
21 | .EXAMPLE
22 | Get-CameraGroupDeviceCount -IncludeCameraNames
23 |
24 | In addition to showing the number of cameras in each group, the report will also include the camera names.
25 | #>
26 |
27 | param(
28 | [Parameter(Mandatory = $false)]
29 | [switch] $IncludeCameraNames
30 | )
31 |
32 | Import-Module -Name MilestonePSTools
33 |
34 | # Get Camera groups
35 | $ms = Get-VmsManagementServer
36 | $cameraGroups = $ms.CameraGroupFolder.CameraGroups
37 | $cameraGroupInfo = New-Object System.Collections.Generic.List[PSCustomObject]
38 |
39 | # Call recursive function and only return the total camera count of the system.
40 | $totalCameraCount = Get-CameraGroupDeviceCountRecursive "" ([ref]$cameraGroupInfo) ([ref]$cameraGroups)
41 |
42 | $row = [PSCustomObject]@{
43 | 'Group' = "!All Cameras"
44 | 'CameraCount' = 0
45 | 'CameraCountIncludingSublevels' = $totalCameraCount
46 | 'CameraName' = $null
47 | }
48 | $cameraGroupInfo.Add($row)
49 |
50 | $cameraGroupInfo | Sort-Object Group, CameraCount, CameraName
51 | }
52 |
53 | function Get-CameraGroupDeviceCountRecursive ([string]$path,[ref]$cameraGroupInfo,[ref]$cameraGroups)
54 | {
55 | <#
56 | .SYNOPSIS
57 | Don't run this script directly. It gets run by Get-CameraGroupDeviceCount.
58 | Recursively goes through each camera group to get the camera counts and names.
59 |
60 | .DESCRIPTION
61 | Don't run this script directly. It gets run by Get-CameraGroupDeviceCount.
62 | Recursively goes through each camera group to get the camera counts and names.
63 | #>
64 |
65 | $individualCameraCount = 0
66 | $combinedCameraCount = 0
67 | $totalCameraCount = 0
68 |
69 | if ($null -ne $cameraGroups.value.Name)
70 | {
71 | foreach ($cameraGroup in $cameraGroups.value)
72 | {
73 | $individualCameraCount = $cameraGroup.CameraFolder.Cameras.Count
74 | $combinedCameraCount = $individualCameraCount + (Get-CameraGroupDeviceCountRecursive "$($path)/$($cameraGroup.Name)" ([ref]$cameraGroupInfo.value) ([ref]$cameraGroup.CameraGroupFolder.CameraGroups))
75 | $row = [PSCustomObject]@{
76 | 'Group' = "$($path)/$($cameraGroup.Name)"
77 | 'CameraCount' = $individualCameraCount
78 | 'CameraCountIncludingSublevels' = $combinedCameraCount
79 | 'CameraName' = $null
80 | }
81 | $cameraGroupInfo.value.Add($row)
82 | $totalCameraCount += $combinedCameraCount
83 |
84 | if ($IncludeCameraNames)
85 | {
86 | $cameras = $cameraGroup.CameraFolder.Cameras
87 |
88 | foreach ($camera in $cameras)
89 | {
90 | $row = [PSCustomObject]@{
91 | 'Group' = "$($path)/$($cameraGroup.Name)"
92 | 'CameraCount' = "xx"
93 | 'CameraCountIncludingSublevels' = $null
94 | 'CameraName' = $camera.Name
95 | }
96 | $cameraGroupInfo.value.Add($row)
97 | }
98 | }
99 | }
100 | }
101 | return $totalCameraCount
102 | }
--------------------------------------------------------------------------------
/Samples/Get-ItemState.ps1:
--------------------------------------------------------------------------------
1 | $itemKinds = @(
2 | [VideoOS.Platform.Kind]::Camera,
3 | [VideoOS.Platform.Kind]::Hardware)
4 |
5 |
6 | $list = New-Object System.Collections.Generic.List[object]
7 | foreach ($result in Get-ItemState) {
8 |
9 | if (-not $itemKinds.Contains($result.FQID.Kind)) {
10 | continue
11 | }
12 |
13 | $item = $result.FQID | Get-PlatformItem
14 |
15 | $address = $null
16 | if ($result.State -ne "Responding") {
17 | if ($result.FQID.Kind -eq [VideoOS.Platform.Kind]::Hardware) {
18 | $address = (Get-Hardware -HardwareId $result.FQID.ObjectId).Address
19 | }
20 | else {
21 | $camera = Get-Camera -Id $result.FQID.ObjectId
22 | $hardware = Get-ConfigurationItem -Path $camera.ParentItemPath
23 | $address = ($hardware.Properties | Where-Object Key -eq Address).Value
24 | }
25 | }
26 |
27 | $entry = [pscustomobject]@{
28 | Name = $item.Name
29 | State = $result.State
30 | Type = [VideoOS.Platform.Kind]::DefaultTypeToNameTable[$result.FQID.Kind]
31 | Address = $address
32 | }
33 |
34 | $entry
35 | $list.Add($entry)
36 | }
37 |
38 | $list | Out-GridView
--------------------------------------------------------------------------------
/Samples/Get-UsersInRoles.ps1:
--------------------------------------------------------------------------------
1 | function Get-UsersInRoles {
2 | <#
3 | .SYNOPSIS
4 | Returns users/groups, the role they are in, and the type of user.
5 | .DESCRIPTION
6 | Returns all users/groups, the role they are in, and the type of user (WindowsUser, WindowsGroup, and BasicUser). WindowsUser
7 | and WindowsGroup could be either Windows or Active Directory. If it is a group, it will only display the group. It will
8 | not display the users in the group.
9 | .EXAMPLE
10 | PS C:\> Get-UsersInRoles
11 | Returns all users/groups from all roles
12 | .EXAMPLE
13 | PS C:\> Get-UsersInRoles -RoleName "Administrators"
14 | Returns all of the users/groups that are in the Administrators role
15 | .EXAMPLE
16 | PS C:\> Get-UsersInRoles | Out-GridView
17 | Outputs the results to a GridView pop-up.
18 | .EXAMPLE
19 | PS C:\> Get-UsersInRoles | Export-Csv -Path C:\CsvExports\Users.csv -NoTypeInformation
20 | Exports the information to a CSV file called Users.csv located at C:\CsvExports. The CsvExports folder must exist before running the command.
21 | #>
22 | [CmdletBinding()]
23 | param (
24 | # Specifies the name of the role. Supports wildcard characters. Default is * and returns all roles.
25 | [Parameter()]
26 | [ValidateNotNullOrEmpty()]
27 | [string]
28 | $RoleName = '*'
29 | )
30 |
31 | process {
32 | $resultFound = $false
33 | foreach ($role in Get-VmsRole -Name $RoleName)
34 | {
35 | $resultFound = $true
36 | foreach ($user in $role.UserFolder.Users)
37 | {
38 | [pscustomobject]@{
39 | Role = $role.Name
40 | User = $user.AccountName
41 | Domain = $user.Domain
42 | IdentityType = $user.IdentityType
43 | }
44 | }
45 | }
46 | if (-not $resultFound -and -not [system.management.automation.wildcardpattern]::ContainsWildcardCharacters($RoleName)) {
47 | Write-Error "Cannot find role '$RoleName' because it does not exist."
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/Samples/Get-VmsCameraPosition.ps1:
--------------------------------------------------------------------------------
1 | function Get-VmsCameraPosition {
2 | <#
3 | .SYNOPSIS
4 | Returns position data (latitude, longitude, field of view, etc.) for each camera in the system
5 | .DESCRIPTION
6 | For each camera in the system, this function returns the camera name, latitude, longitude, Field of View (in degrees),
7 | the direction the camera is facing both in Cardinal (i.e. N, NW, SE, etc.) and Degrees, and depth of the Field of View (in feet).
8 |
9 | If latitude and longitude are empty, then the rest of the values are ignored.
10 | .EXAMPLE
11 | Get-VmsCameraPosition
12 | Outputs the following information
13 |
14 | CamName Latitude Longitude FOV (Degrees) Direction (Cardinal) Direction (Degrees) Depth (Feet)
15 | ------- -------- --------- ------------- -------------------- ------------------- ------------
16 | Axis M1125 - Camera 1 45.4170433700828 -122.732230489572 150.74 N 1.43 27.72
17 | Axis P3265-LVE - Camera 1 34.4257539305559 -117.131595665415 72 NE 46.3 231.89
18 | Canon VB-S30D - Camera 1 12.000030728223 -11.0000179326625 72 N 0 65.62
19 | #>
20 |
21 | $camInfo = New-Object System.Collections.Generic.List[PSCustomObject]
22 |
23 | $directionArray = @(
24 | "N"
25 | "NNE"
26 | "NE"
27 | "ENE"
28 | "E"
29 | "ESE"
30 | "SE"
31 | "SSE"
32 | "S"
33 | "SSW"
34 | "SW"
35 | "WSW"
36 | "W"
37 | "WNW"
38 | "NW"
39 | "NNW"
40 | )
41 |
42 | foreach ($cam in Get-VmsCamera)
43 | {
44 | $gisPoint = $cam.GisPoint
45 | if ($gisPoint.Substring(7, $GisPoint.Length - 8) -match "[0-9]")
46 | {
47 | $long = $gisPoint.Substring(7, $GisPoint.Length - 8).Split(" ")[0]
48 | $lat = $gisPoint.Substring(7, $GisPoint.Length - 8).Split(" ")[1]
49 | } else
50 | {
51 | $long = $null
52 | $lat = $null
53 | }
54 |
55 | if ($gisPoint -eq "POINT EMPTY")
56 | {
57 | $fov = $null
58 | $directionCardinal = $null
59 | $directionDegrees = $null
60 | $depth = $null
61 | } else
62 | {
63 | $fov = $cam.CoverageFieldOfView * 360
64 |
65 | # Sometimes degrees is stored as 0 to 360 and sometimes it is stored as -180 to 180.
66 | # Both need to be accounted for.
67 | if ($cam.CoverageDirection -ge 0)
68 | {
69 | $directionDegrees = $cam.CoverageDirection * 360
70 | } elseif ($cam.CoverageDirection -lt 0)
71 | {
72 | $directionDegrees = 360 + ($cam.CoverageDirection * 360)
73 | }
74 |
75 | if (($cam.CoverageDirection * 360) -ne 360)
76 | {
77 | $index = [math]::Floor($directionDegrees / 22.5)
78 | $directionCardinal = $directionArray[$index]
79 | } elseif (($cam.CoverageDirection * 360) -eq 360)
80 | {
81 | $directionCardinal = "N"
82 | } else
83 | {
84 | $directionCardinal = $null
85 | }
86 | $depth = $cam.CoverageDepth * 3.28084
87 | }
88 |
89 | $row = [PSCustomObject]@{
90 | CamName = $cam.Name
91 | Latitude = $lat
92 | Longitude = $long
93 | "FOV (Degrees)" = $fov
94 | "Direction (Cardinal)" = $directionCardinal
95 | "Direction (Degrees)" = if ($null -ne $directionDegrees) {[math]::Round($directionDegrees,2)} else {$null}
96 | "Depth (Feet)" = if ($null -ne $depth) {[math]::Round($depth,2)} else {$null}
97 | }
98 | $camInfo.Add($row)
99 | }
100 | $camInfo
101 | }
--------------------------------------------------------------------------------
/Samples/Group-CamerasByModel.ps1:
--------------------------------------------------------------------------------
1 | function Group-CamerasByModel {
2 | <#
3 | .SYNOPSIS
4 | Creates a camera group for each camera make and model.
5 |
6 | .DESCRIPTION
7 | Creates a specified base camera group, and within that base group, it creates a group for every enabled camera
8 | make/model. For each camera model, a group is created with a name like 1-X where X is the number of cameras in that
9 | group. If the number exceeds 400, it will create another group called 401-X, until all enabled cameras of that model
10 | have been added to a group.
11 |
12 | Groups larger than 400 should not be created as the Management Client will not be able to do bulk configurations on them.
13 |
14 | Having groups of camera models is very useful for doing bulk configurations. Note that in some instances, cameras
15 | report their models as a series of cameras and not a specific model. In cases like this, bulk configuration will
16 | likely not work as the cameras in the series might support different resolutions and/or frame rates.
17 |
18 | .PARAMETER BaseGroupPath
19 | Specifies the camera group under which cameras should be grouped by model. For example, "/__ADMIN__/Models".
20 |
21 | .PARAMETER MaxGroupSize
22 | Specifies the maximum number of cameras to add to a single camera group. The default value is 400 which is also the maximum
23 | number of cameras the Management Client will allow bulk configuration operations on.
24 |
25 | .EXAMPLE
26 | Group-CamerasByModel -BaseGroupPath /__ADMIN__/Models
27 |
28 | Creates a top-level group called "__ADMIN__" which will typically be listed at the top of the device group list, and
29 | a "Models" subgroup. Cameras are then grouped by model under the Models camera subgroup.
30 |
31 | .EXAMPLE
32 | Group-CamerasByModel -BaseGroupPath /zzADMIN/Models -MaxGroupSize 200
33 |
34 | Creates a top-level group called "zzADMIN__" which will typically be listed at the bottom of the device group list, and
35 | a "Models" subgroup. Cameras are then grouped by model under the Models camera subgroup with a maximum group size of
36 | 200 cameras.
37 |
38 | .NOTES
39 | For reference, on a 5217 camera test system with 148 unique models, this function completes in 6 minutes 13 seconds.
40 | The time required to run in your environment may vary based on many factors including total number of cameras and
41 | models, management server and/or sql server load, and latency between the PowerShell session and the management server.
42 | #>
43 | [CmdletBinding()]
44 | param (
45 | [Parameter(Position = 0, ValueFromPipelineByPropertyName, Mandatory)]
46 | [string]
47 | $BaseGroupPath,
48 |
49 | [Parameter(Position = 1, ValueFromPipelineByPropertyName)]
50 | [ValidateRange(1, 400)]
51 | [int]
52 | $MaxGroupSize = 400
53 | )
54 |
55 | process {
56 | $parentProgress = @{
57 | Activity = 'Creating camera groups by model'
58 | Status = 'Discovering camera models'
59 | Id = Get-Random
60 | PercentComplete = 0
61 | }
62 | $childProgress = @{
63 | Activity = 'Populating camera groups'
64 | Id = Get-Random
65 | ParentId = $parentProgress.Id
66 | PercentComplete = 0
67 | }
68 | try {
69 | Write-Progress @parentProgress
70 |
71 | Write-Verbose "Removing camera group '$BaseGroupPath' if present"
72 | Clear-VmsCache
73 | Get-VmsDeviceGroup -Path $BaseGroupPath -ErrorAction SilentlyContinue | Remove-VmsDeviceGroup -Recurse -Confirm:$false -ErrorAction Stop
74 |
75 | Write-Verbose 'Discovering all enabled cameras'
76 | $ms = [VideoOS.Platform.ConfigurationItems.ManagementServer]::new((Get-VmsSite).FQID.ServerId)
77 | $filters = 'RecordingServer', 'Hardware', 'Camera' | ForEach-Object {
78 | [VideoOS.ConfigurationApi.ClientService.ItemFilter]::new($_, $null, 'Enabled')
79 | }
80 | $ms.FillChildren($filters.ItemType, $filters)
81 |
82 | $parentProgress.Status = 'Grouping and sorting cameras'
83 | Write-Progress @parentProgress
84 |
85 | Write-Verbose 'Sorting cameras by model'
86 | $modelGroups = $ms.RecordingServerFolder.RecordingServers.HardwareFolder.Hardwares | Group-Object Model | Sort-Object Name
87 | $totalCameras = ($modelGroups.Group.CameraFolder.Cameras).Count
88 | $camerasProcessed = 0
89 |
90 | $parentProgress.Status = 'Processing'
91 | Write-Progress @parentProgress
92 |
93 | foreach ($group in $modelGroups) {
94 | $modelName = $group.Name
95 | $safeModelName = $modelName.Replace('/', '`/')
96 |
97 | $cameras = $group.Group.CameraFolder.Cameras | Sort-Object Name
98 | $totalForModel = $cameras.Count
99 |
100 | $groupNumber = $positionInGroup = 1
101 | $group = $null
102 |
103 | $childProgress.Status = "Current: $BaseGroupPath/$modelName"
104 | $parentProgress.PercentComplete = $camerasProcessed / $totalCameras * 100
105 | Write-Progress @parentProgress
106 |
107 | Write-Verbose "Creating groups for $totalForModel cameras of model '$modelName'"
108 | for ($i = 0; $i -lt $totalForModel; $i++) {
109 | $childProgress.PercentComplete = $i / $totalForModel * 100
110 | Write-Progress @childProgress
111 | if ($null -eq $group) {
112 | $first = $groupNumber * $MaxGroupSize - ($MaxGroupSize - 1)
113 | $last = $groupNumber * $MaxGroupSize
114 | if ($totalForModel - ($i + 1) -lt $MaxGroupSize) {
115 | $last = $totalForModel
116 | }
117 | $groupName = '{0}-{1}' -f $first, $last
118 | Write-Verbose "Creating group $BaseGroupPath/$modelName/$groupName"
119 | $group = New-VmsDeviceGroup -Type Camera -Path "$BaseGroupPath/$safeModelName/$groupName"
120 | }
121 |
122 | Add-VmsDeviceGroupMember -Group $group -Device $cameras[$i]
123 |
124 | $camerasProcessed++
125 | $positionInGroup++
126 | if ($positionInGroup -gt $MaxGroupSize) {
127 | $group = $null
128 | $positionInGroup = 1
129 | $groupNumber++
130 | }
131 | }
132 | }
133 | } finally {
134 | $childProgress.Completed = $true
135 | Write-Progress @childProgress
136 |
137 | $parentProgress.Completed = $true
138 | Write-Progress @parentProgress
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/Samples/Import-GPS-Coordinates/Import-GpsCoordinates.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Samples/Import-GPS-Coordinates/Import-GpsCoordinates.png
--------------------------------------------------------------------------------
/Samples/Import-GPS-Coordinates/Import-GpsCoordinates.ps1:
--------------------------------------------------------------------------------
1 | function Import-GpsCoordinates {
2 | <#
3 | .SYNOPSIS
4 | Updates the GPS coordinates of cameras in Milestone based on a CSV file with MAC addresses
5 | and the corresponding GPS coordinates.
6 |
7 | .DESCRIPTION
8 | This sample function allows you to override the default column names of "MAC" and
9 | "Coordinates" if desired, and the coordinates are expected to be found in
10 | "latitude, longitude" format with no special characters except the separating comma.
11 |
12 | .PARAMETER Path
13 | The path to the source CSV file to use as input.
14 |
15 | .PARAMETER MacColumn
16 | Override the default column name to look for in the CSV file for the hardware MAC address.
17 | Default: MAC
18 |
19 | .PARAMETER CoordinateColumn
20 | Override the default column name to look for in the CSV file for the camera coordinates address.
21 | Default: Coordinates
22 |
23 | .EXAMPLE
24 | Import-GpsCoordinates -Path .\coordinates.csv
25 |
26 | Uses coordinates.csv to update the GisPoint property of all cameras who's parent Hardware object
27 | has a MAC address value found in the CSV file.
28 | #>
29 | [CmdletBinding()]
30 | param (
31 | # CSV file Path
32 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
33 | [string]
34 | $Path,
35 | # Optional: MAC Address column name. Default: MAC
36 | [Parameter()]
37 | [string]
38 | $MacColumn = "MAC",
39 | # Optional: Coordinate column name. Default: Coordinates
40 | [Parameter()]
41 | [string]
42 | $CoordinateColumn = "Coordinates"
43 | )
44 |
45 | process {
46 | $source = Import-Csv -Path $Path
47 | foreach ($hardware in Get-Hardware) {
48 | $mac = ($hardware | Get-HardwareSetting).MacAddress
49 | $matchingRow = $source | Where-Object $MacColumn -eq $mac
50 | if ($null -eq $matchingRow) {
51 | Write-Warning "No row found in CSV file matching MAC address '$mac'"
52 | continue
53 | }
54 |
55 | $lat, $long = $matchingRow.$CoordinateColumn -split ","
56 | $point = "POINT ($long $lat)"
57 | foreach ($camera in $hardware | Get-Camera) {
58 | $camera.GisPoint = $point
59 | $camera.Save()
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/Samples/Import-GPS-Coordinates/README.md:
--------------------------------------------------------------------------------
1 | ## Import-GpsCoordinates
2 | Update the GPS coordinates of cameras in Milestone based on a CSV file
3 |
4 |
5 |
6 |
7 | ### Demo
8 |
9 | See [This demo](https://youtu.be/o5XK-WZRZYY) on YouTube where a simplified version of this sample is written and demonstrated from scratch.
10 |
11 | ### Documentation
12 |
13 | ```powershell
14 | PS C:\demo> Get-Help Import-GpsCoordinates -Full
15 |
16 | NAME
17 | Import-GpsCoordinates
18 |
19 | SYNOPSIS
20 | Updates the GPS coordinates of cameras in Milestone based on a CSV file with MAC addresses
21 | and the corresponding GPS coordinates.
22 |
23 |
24 | SYNTAX
25 | Import-GpsCoordinates [-Path] [[-MacColumn] ] [[-CoordinateColumn] ] []
26 |
27 |
28 | DESCRIPTION
29 | This sample function allows you to override the default column names of "MAC" and
30 | "Coordinates" if desired, and the coordinates are expected to be found in
31 | "latitude, longitude" format with no special characters except the separating comma.
32 |
33 |
34 | PARAMETERS
35 | -Path
36 | The path to the source CSV file to use as input.
37 |
38 | Required? true
39 | Position? 1
40 | Default value
41 | Accept pipeline input? true (ByValue, ByPropertyName)
42 | Accept wildcard characters? false
43 |
44 | -MacColumn
45 | Override the default column name to look for in the CSV file for the hardware MAC address.
46 | Default: MAC
47 |
48 | Required? false
49 | Position? 2
50 | Default value MAC
51 | Accept pipeline input? false
52 | Accept wildcard characters? false
53 |
54 | -CoordinateColumn
55 | Override the default column name to look for in the CSV file for the camera coordinates address.
56 | Default: Coordinates
57 |
58 | Required? false
59 | Position? 3
60 | Default value Coordinates
61 | Accept pipeline input? false
62 | Accept wildcard characters? false
63 |
64 |
65 | This cmdlet supports the common parameters: Verbose, Debug,
66 | ErrorAction, ErrorVariable, WarningAction, WarningVariable,
67 | OutBuffer, PipelineVariable, and OutVariable. For more information, see
68 | about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
69 |
70 | INPUTS
71 |
72 | OUTPUTS
73 |
74 | -------------------------- EXAMPLE 1 --------------------------
75 |
76 | PS C:\>Import-GpsCoordinates -Path .\coordinates.csv
77 |
78 | Uses coordinates.csv to update the GisPoint property of all cameras who's parent Hardware object
79 | has a MAC address value found in the CSV file.
80 |
81 |
82 |
83 |
84 |
85 | RELATED LINKS
86 | ```
--------------------------------------------------------------------------------
/Samples/ImportFromCsvWithPermissions.ps1:
--------------------------------------------------------------------------------
1 | # This sample demonstrates one way you can import cameras from a CSV file and
2 | # grant some basic read permissions to a pipe-delimited set of roles. Errors
3 | # will be logged as warnings to the console and any failed records will be
4 | # saved in the $failed variable.
5 | #
6 | # It is assumed you are already logged in to your VMS, and you have a CSV file
7 | # prepared which resembles the example below:
8 | #
9 | # Address,UserName,Password,RecordingServer,Name,Group,Roles
10 | # "http://192.168.1.100","root","pass","Recorder1","Driveway","/Outdoor","Operators|Security"
11 |
12 |
13 | $newHardwareRows = Import-Csv -Path .\newhardware.csv
14 | $failed = New-Object System.Collections.Generic.List[pscustomobject]
15 | foreach ($record in $newHardwareRows)
16 | {
17 | try {
18 | $recorder = Get-RecordingServer -Name $record.RecordingServer
19 | $params = @{
20 | Address = $record.Address
21 | UserName = $record.UserName
22 | Password = $record.Password
23 | Name = $record.Name
24 | GroupPath = $record.Group
25 | }
26 | $hw = $recorder | Add-Hardware @params -Enabled
27 |
28 | foreach ($camera in $hw | Get-Camera) {
29 | foreach ($roleName in $record.Roles.Split('|')) {
30 | $acl = $camera | Get-DeviceAcl -RoleName $roleName
31 |
32 | $acl.SecurityAttributes["GENERIC_READ"] = "True"
33 | $acl.SecurityAttributes["VIEW_LIVE"] = "True"
34 | $acl.SecurityAttributes["PLAYBACK"] = "True"
35 | $acl.SecurityAttributes["PTZ_CONTROL"] = "True"
36 | $acl.SecurityAttributes["READ_BOOKMARKS"] = "True"
37 | $acl.SecurityAttributes["READ_EVIDENCE_LOCK"] = "True"
38 | $acl.SecurityAttributes["READ_SEQUENCES"] = "True"
39 |
40 | $acl | Set-DeviceAcl
41 | }
42 | }
43 | }
44 | catch {
45 | Write-Warning "Error adding $($record.Address): $($_.Message)"
46 | $failed.Add($record)
47 | }
48 | }
--------------------------------------------------------------------------------
/Samples/ReportOnTransactSources.ps1:
--------------------------------------------------------------------------------
1 | # Retrieve all Transact Sources using the TCP Client Transact Connector and
2 | # output the name, host, port and retention properties.
3 | # NOTE: This requires MilestonePSTools 1.0.71 or greater.
4 |
5 | # Items in a VMS configuration have a 'Kind' property which defines what type
6 | # of object it is, or what types of objects it contains in the event it's a
7 | # container with child items in it. We need the GUID value used for items of
8 | # Transact type or 'Kind'.
9 | $transactKind = (Get-Kind -List | Where-Object DisplayName -eq Transact).Kind
10 |
11 | foreach ($item in Get-PlatformItem -Kind $transactKind) {
12 | # The value a20d8523-547b-4bb3-b5aa-7899c62c68f6 is the ID for the built-in
13 | # TCP Client transact connector. This script ignores other connectors as
14 | # they may have different property names.
15 | if ($item.Properties['TransactConnectorId'] -notlike "a20d8523-547b-4bb3-b5aa-7899c62c68f6") {
16 | continue
17 | }
18 |
19 | $result = [pscustomobject]@{
20 | Name = $item.Name
21 | Host = $item.Properties["_HOST"]
22 | Port = $item.Properties["_PORT"]
23 | Retention = $item.Properties["Retention"]
24 | }
25 |
26 | Write-Output $result
27 | }
--------------------------------------------------------------------------------
/Samples/Reporting/Get-RecorderReport.ps1:
--------------------------------------------------------------------------------
1 | function Get-RecorderReport {
2 | [CmdletBinding()]
3 | param (
4 | # Specifies one or more Recording Servers from which to generate a camera report. By default all Recording Servers will be used.
5 | [Parameter(ValueFromPipeline)]
6 | [VideoOS.Platform.ConfigurationItems.RecordingServer[]]
7 | $RecordingServer
8 | )
9 |
10 | begin {
11 | $runspacepool = [runspacefactory]::CreateRunspacePool(4, 16)
12 | $runspacepool.Open()
13 | $threads = New-Object System.Collections.Generic.List[pscustomobject]
14 |
15 | $process = {
16 | param(
17 | [VideoOS.Platform.ConfigurationItems.RecordingServer]$Recorder
18 | )
19 | try {
20 | $hardware = $recorder | Get-VmsHardware
21 | $cameras = $hardware | Get-VmsCamera -EnableFilter All
22 | $enabledHardware = $hardware | Where-Object Enabled
23 | $enabledCameras = $cameras | Where-Object { $_.Enabled -and $_.ParentItemPath -in $enabledHardware.Path }
24 |
25 | $obj = [PSCustomObject]@{
26 | RecordingServer = $recorder.Name
27 | TotalHardware = $hardware.Count
28 | EnabledHardware = $enabledHardware.Count
29 | TotalCameras = $cameras.Count
30 | EnabledCameras = $enabledCameras.Count
31 | CamerasStarted = 0
32 | UsedSpaceInBytes = 0
33 | RecordedBPS = 0
34 | TotalBPS = 0
35 | OverflowCount = 0
36 | CamerasWithErrors = 0
37 | CamerasNotConnected = 0
38 | DatabaseRepairsInProgress = 0
39 | DatabaseWriteErrors = 0
40 | CamerasNotLicensed = 0
41 | }
42 |
43 | try {
44 | $svc = $recorder | Get-RecorderStatusService2
45 | $stats = $svc.GetVideoDeviceStatistics((Get-VmsToken), [guid[]]$enabledCameras.Id)
46 | $status = $svc.GetCurrentDeviceStatus((Get-VmsToken), [guid[]]$enabledCameras.Id)
47 | $liveStreams = $stats.VideoStreamStatisticsArray | Where-Object RecordingStream -eq $false
48 | $recordedStreams = $stats.VideoStreamStatisticsArray | Where-Object RecordingStream
49 | $recordedBPS = $recordedStreams | Measure-Object -Property BPS -Sum | Select-Object -ExpandProperty Sum
50 |
51 | $obj.CamerasStarted = ($status.CameraDeviceStatusArray | Where-Object Started).Count
52 | $obj.UsedSpaceInBytes = $stats | Measure-Object -Property UsedSpaceInBytes -Sum | Select-Object -ExpandProperty Sum
53 | $obj.RecordedBPS = $recordedBPS
54 | $obj.TotalBPS = $recordedBPS + ( $liveStreams | Measure-Object -Property BPS -Sum | Select-Object -ExpandProperty Sum )
55 | $obj.OverflowCount = ($status.CameraDeviceStatusArray | Where-Object ErrorOverflow).Count
56 | $obj.CamerasWithErrors = ($status.CameraDeviceStatusArray | Where-Object Error).Count
57 | $obj.CamerasNotConnected = ($status.CameraDeviceStatusArray | Where-Object ErrorNoConnection).Count
58 | $obj.DatabaseRepairsInProgress = ($status.CameraDeviceStatusArray | Where-Object DbRepairInProgress).Count
59 | $obj.DatabaseWriteErrors = ($status.CameraDeviceStatusArray | Where-Object ErrorWritingGop).Count
60 | $obj.CamerasNotLicensed = ($status.CameraDeviceStatusArray | Where-Object CamerasNotLicensed).Count
61 | }
62 | catch {
63 | Write-Error -Exception $_.Exception -Message "Error collecting statistics from $($recorder.Name) ($($recorder.Hostname))"
64 | }
65 |
66 | Write-Output $obj
67 | }
68 | catch {
69 | Write-Error -Exception $_.Exception -Message "Unexpected error: $($_.Message). $($recorder.Name) ($($recorder.Hostname)) will not be included in the report."
70 | }
71 | finally {
72 | $svc.Dispose()
73 | }
74 | }
75 | }
76 |
77 | process {
78 | $progressParams = @{
79 | Activity = $MyInvocation.MyCommand.Name
80 | CurrentOperation = ''
81 | PercentComplete = 0
82 | Completed = $false
83 | }
84 |
85 | if ($null -eq $RecordingServer) {
86 | $RecordingServer = Get-VmsRecordingServer
87 | }
88 |
89 | try {
90 | foreach ($recorder in $RecordingServer) {
91 | $ps = [powershell]::Create()
92 | $ps.RunspacePool = $runspacepool
93 | $asyncResult = $ps.AddScript($process).AddParameters(@{
94 | Recorder = $recorder
95 | }).BeginInvoke()
96 | $threads.Add([pscustomobject]@{
97 | PowerShell = $ps
98 | Result = $asyncResult
99 | })
100 | }
101 |
102 | if ($threads.Count -eq 0) {
103 | return
104 | }
105 |
106 | $progressParams.CurrentOperation = 'Processing requests for recorder information'
107 | $completedThreads = New-Object System.Collections.Generic.List[pscustomobject]
108 | $totalJobs = $threads.Count
109 | while ($threads.Count -gt 0) {
110 | $progressParams.PercentComplete = ($totalJobs - $threads.Count) / $totalJobs * 100
111 | $progressParams.Status = "Processed $($totalJobs - $threads.Count) out of $totalJobs requests"
112 | Write-Progress @progressParams
113 | foreach ($thread in $threads) {
114 | if ($thread.Result.IsCompleted) {
115 | $thread.PowerShell.EndInvoke($thread.Result)
116 | $thread.PowerShell.Dispose()
117 | $completedThreads.Add($thread)
118 | }
119 | }
120 | $completedThreads | Foreach-Object { [void]$threads.Remove($_)}
121 | $completedThreads.Clear()
122 | if ($threads.Count -eq 0) {
123 | break;
124 | }
125 | Start-Sleep -Seconds 1
126 | }
127 | }
128 | finally {
129 | if ($threads.Count -gt 0) {
130 | Write-Warning "Stopping $($threads.Count) running PowerShell instances. This may take a minute. . ."
131 | foreach ($thread in $threads) {
132 | $thread.PowerShell.Dispose()
133 | }
134 | }
135 | $runspacepool.Close()
136 | $runspacepool.Dispose()
137 | $progressParams.Completed = $true
138 | Write-Progress @progressParams
139 | }
140 | }
141 | }
--------------------------------------------------------------------------------
/Samples/Reporting/Get-RoleReport.ps1:
--------------------------------------------------------------------------------
1 | function Get-RoleReport {
2 | <#
3 | .SYNOPSIS
4 | Gets a list of all cameras and the permissions associated with the specified role
5 | .DESCRIPTION
6 | Device permissions are saved either as "overall security attributes" at the role level, or they
7 | are associated with each individual device, or a mix of the two. This report helps consolidate
8 | permissions into a verbose report where each row represents a role and it's effective permissions
9 | on a specific camera.
10 | .EXAMPLE
11 | PS C:\> Get-RoleReport -RoleName 'Guards' | Export-Csv ~\Desktop\Role-Report.csv
12 | Creates a CSV containing a list of all devices the 'Guards' role has at least 'GENERIC_READ' access to.
13 | .EXAMPLE
14 | PS C:\> Get-RoleReport -IncludeNoAccess | Export-Csv ~\Desktop\Role-Report.csv
15 | Creates a CSV containing a list of all roles and their permissions on all devices. Devices where the role has no access will be included in the report.
16 | .NOTES
17 | This report is not meaningful for the Administrator role, so if your RoleName filter returns only the built-in Administrators role, you will receive an error.
18 | #>
19 | [CmdletBinding()]
20 | param(
21 | # Specifies the name of the role. Supports wildcard characters. Default is * and returns all roles.
22 | [Parameter()]
23 | [ValidateNotNullOrEmpty()]
24 | [string]
25 | $RoleName = '*',
26 |
27 | # Specifies that rows where the role lacks read access should still be included in the report.
28 | [Parameter()]
29 | [switch]
30 | $IncludeNoAccess
31 | )
32 |
33 | process {
34 | $roles = Get-VmsRole -Name $RoleName -ErrorAction Stop | Where-Object RoleType -eq UserDefined
35 | $cameras = Get-VmsCamera
36 | if ($null -eq $roles -or $roles.Count -eq 0) {
37 | Write-Error "Cannot find user-defined role '$RoleName' because it does not exist."
38 | }
39 |
40 | $overallSecurity = @{}
41 | foreach($role in $roles) {
42 | $securityAttributes = @{}
43 | $invokeInfo = $role.ChangeOverallSecurityPermissions('623d03f8-c5d5-46bc-a2f4-4c03562d4f85')
44 | foreach ($key in $invokeInfo.GetPropertyKeys()) {
45 | $securityAttributes.$key = $invokeInfo.GetProperty($key)
46 | }
47 | $overallSecurity.($role.Id) = $securityAttributes
48 | }
49 |
50 | $rowsCompleted = 0
51 | $totalRows = $cameras.Count * $roles.Count
52 | try {
53 | foreach ($camera in $cameras) {
54 | foreach ($role in $roles) {
55 | Write-Progress -Activity "Auding camera permissions per role" -PercentComplete ([int]($rowsCompleted / $totalRows * 100))
56 | $acl = $camera | Get-DeviceAcl -Role $role
57 | if ($IncludeNoAccess -or $overallSecurity.($role.Id).'GENERIC_READ' -eq 'Allow' -or $acl.SecurityAttributes.GENERIC_READ -eq 'True') {
58 | $row = [ordered]@{
59 | Role = $role.Name
60 | Camera = $camera.Name
61 | }
62 | foreach ($key in $acl.SecurityAttributes.Keys) {
63 | $overallSecurityAttribute = $overallSecurity.($role.Id).$key
64 | if ($overallSecurityAttribute -eq 'None') {
65 | $row.$key = $acl.SecurityAttributes.$key
66 | }
67 | else {
68 | $row.$key = $overallSecurityAttribute -eq 'Allow'
69 | }
70 | }
71 | $row.RoleId = $role.Id
72 | $row.CameraId = $camera.Id
73 |
74 | Write-Output ([pscustomobject]$row)
75 | }
76 | $rowsCompleted++
77 | }
78 | }
79 | }
80 | finally {
81 | Write-Progress -Activity "Auding camera permissions per role" -Completed
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/Samples/Reporting/Get-VmsCameraDiskUsage.ps1:
--------------------------------------------------------------------------------
1 | #Requires -Modules MilestonePSTools
2 | function Get-VmsCameraDiskUsage {
3 | <#
4 | .SYNOPSIS
5 | Gets the amount of space used for all cameras
6 | .DESCRIPTION
7 | Gets the amount of space used for all cameras
8 | .EXAMPLE
9 | PS C:\> Get-VmsCameraDiskUsage | Out-GridView
10 | Gets the disk usage information for all cameras and all storages used by those cameras and displays them in a grid view
11 | #>
12 |
13 | begin {
14 | $offlineRecorders = New-Object System.Collections.Generic.List[PSCustomObject]
15 | $svc = Get-IServerCommandService
16 | $config = $svc.GetConfiguration((Get-Token))
17 | }
18 |
19 | process {
20 | #$config.Recorders | ForEach-Object {
21 | foreach ($recInfo in $config.Recorders) {
22 | #$recInfo = $_
23 | $statusSvc = Get-RecorderStatusService2 -RecordingServer (Get-VmsRecordingServer -Id $recInfo.RecorderId) -ErrorAction SilentlyContinue
24 | try {
25 | $stats = $statusSvc.GetVideoDeviceStatistics((Get-Token), $recInfo.Cameras.DeviceId)
26 | } catch {
27 | $offlineRecorders.Add($recInfo.Name)
28 | }
29 |
30 | $statsHash = @{}
31 | $stats | ForEach-Object {
32 | $statsHash[$_.DeviceId] = $_
33 | }
34 |
35 | foreach ($hw in $recInfo.Hardware){
36 | if ($hw.Disabled -eq $true) {
37 | Break
38 | }
39 | foreach ($cam in Get-VmsCamera -Hardware (Get-VmsHardware -Id $hw.HardwareId) -EnableFilter Enabled) {
40 | if (-not [string]::IsNullOrEmpty($statsHash[[guid]$cam.Id])) {
41 | [pscustomobject]@{
42 | Camera = $cam.Name
43 | Hardware = $hw.Name
44 | Recorder = $recInfo.Name
45 | 'UsedSpace(GB)' = [math]::Round(($statsHash[[guid]$cam.Id] | Select-Object -Expand UsedSpaceInBytes) / 1GB,2)
46 | }
47 | } else {
48 | [pscustomobject]@{
49 | Camera = $cam.Name
50 | Hardware = $hw.Name
51 | Recorder = $recInfo.Name
52 | 'UsedSpace(GB)' = "Unknown"
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
60 | end {
61 | if (-not [string]::IsNullOrEmpty($offlineRecorders[0])) {
62 | foreach ($offlineRecorder in $offlineRecorders) {
63 | Write-Warning "Recording Server $($offlineRecorder) was unavailable and was skipped."
64 | }
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/Samples/Reporting/README - sp-MX.md:
--------------------------------------------------------------------------------
1 | # Informar es divertido
2 |
3 | Las funciones y los ejemplos de esta carpeta muestran los métodos para recuperar información con fines de generación de informes. Probablemente existan tantos conjuntos diferentes de datos deseados por los clientes y operadores de VMS como instalaciones de VMS en el mundo, por lo que estas herramientas deben combinarse o utilizarse como inspiración según sea necesario para que usted obtenga los datos que necesita.
4 |
5 | ## Una nota sobre las interfaces no compatibles
6 |
7 | Milestone proporciona una gran cantidad de funcionalidades en MIP SDK. Casi todo el módulo MilestonePSTools está escrito mediante componentes puros de MIP SDK. Cambiar y ampliar MIP SDK al tiempo que se maximiza la compatibilidad con los sistemas anteriores o posteriores y se minimizan los cambios importantes es un verdadero desafío y tengo un profundo respeto por el equipo del Kit de desarrollo de software (SDK) por sus esfuerzos para llevar la funcionalidad tan necesaria a las manos de desarrolladores externos.
8 | Sin embargo, existen algunas funciones que el MIP SDK aún no puede realizar y dado que un número muy pequeño de usuarios necesita o desea acceder a estas funciones, el equipo del SDK tardará algún tiempo en abordar esas necesidades e implementar nuevas características con soporte a largo plazo. Mientras tanto, con un poco de investigación y persuasión, ocasionalmente podemos superar estas limitaciones utilizando API que no estaban destinadas a ser utilizadas por el público.
9 | Encontrará varias funciones con ciertas interfaces que no son intuitivas y carecen de documentación en los documentos en línea del MIP SDK. Estas funciones estarán claramente documentadas en los comentarios de ayuda y no se promete absolutamente ningún soporte cuando las interfaces no compatibles utilizadas por estas funciones cambian entre versiones y la compatibilidad con versiones anteriores o posteriores se pierda o empeore.
10 |
11 | ## Get-VmsCameraDiskUsage
12 |
13 | Una solicitud común que actualmente no se ha cumplido con MIP SDK es la capacidad de recuperar la cantidad de almacenamiento utilizada por cámara. Es posible extraer el espacio utilizado/disponible para una ubicación de almacenamiento en directo o de archivo, pero no cámara por cámara.
14 |
15 | –“Pero la información está en Management Client", le escucho decir. “Entonces, ¿por qué no podemos obtenerlo con el SDK? ¿El Management Client no usa el SDK?” Bueno, en realidad no, no es así. Gran parte del Management Client hace uso de interfaces internas donde los desarrolladores tienen la flexibilidad de agregar/cambiar capacidades entre versiones mientras mantienen las interfaces MIP SDK más estables con el tiempo. De todos modos, mucho de lo que se hace en Management Client es irrelevante para la mayoría de los desarrolladores de integración de MIP SDK.
16 |
17 | En pocas palabras, esta función usa una interfaz de cliente WCF que no es compatible para uso externo. El método de uso de esta interfaz es muy similar a las otras interfaces _compatibles_ como IConfigurationService, por ejemplo, y técnicamente cualquier persona que conozca la URL web del servicio puede usarla. La autenticación de la interfaz es la misma que cualquier otra interfaz WCF de Milestone y esta función autentifica y usa esta API de forma transparente en su nombre. Así es como se ve la salida: Los valores de UsedSpace están en bytes, y simplemente ignore los valores de AvailableSpace por ahora. Al parecer puede haber algún tipo de error de desbordamiento en el que estos valores no tengan mucho sentido; es probable que el campo no se use en Management Client. Si necesita conocer AvailableSpace, navegue hasta los objetos de almacenamiento/archivo `$RecordingServer.StorageFolder.Storages[$i]` or `$RecordingServer.StorageFolder.Storages[$i].ArchiveStorageFolder.ArchiveStorages[$j]`.
18 |
19 | ```powershell
20 | PS C:\> Get-Hardware | Get-Camera | Get-VmsCameraDiskUsage | Format-Table *
21 |
22 | CameraId StorageId RecorderId UsedSpace AvailableSpace IsOnline
23 | -------- --------- ---------- --------- -------------- --------
24 | 3c25ff7a-7c76-49bd-bda0-116f5e051e48 cce0ef0f-36b1-4221-964d-e5de1f641741 72080191-d39d-4229-b151-65bcd740c393 4513 157586161664 True
25 | afa846f8-846c-4932-b206-c1c6c24e0b5f def84b4a-1e7a-4f99-ac5f-671ae76d520b 72080191-d39d-4229-b151-65bcd740c393 4519 157586161664 True
26 | c2733741-0c71-4197-89d2-030339c7a9ea def84b4a-1e7a-4f99-ac5f-671ae76d520b 72080191-d39d-4229-b151-65bcd740c393 161278294 157586161664 True
27 | c2733741-0c71-4197-89d2-030339c7a9ea e96f206b-3ecd-421a-906b-e32393b4bedb 72080191-d39d-4229-b151-65bcd740c393 705524466 12260420734976 True
28 | c2733741-0c71-4197-89d2-030339c7a9ea 2358075f-2291-4c43-86c8-9d6351f2ed59 72080191-d39d-4229-b151-65bcd740c393 71165280 197973835776 True
29 |
30 | PS C:\>
31 | ```
32 |
--------------------------------------------------------------------------------
/Samples/Reporting/README.md:
--------------------------------------------------------------------------------
1 | # Reporting is fun
2 |
3 | The functions and examples in this folder demonstrate methods of retrieving information for reporting purposes. I suspect there are as many different sets of data desired by customers and operators of a VMS as there are VMS installations in the world, so these tools should be combined and/or used as inspiration to the extent necessary to get the data you need.
4 |
5 | ## Note about unsupported interfaces
6 |
7 | Milestone provides a ton of functionality in the MIP SDK. Almost the entire MilestonePSTools module is written using pure MIP SDK components. Changing and extending MIP SDK while maximizing backward/forward compatibility and minimizing breaking changes is a challenge and I have a deep respect for the team for their efforts to bring much needed functionality to the hands of 3rd party developers.
8 |
9 | There are things the MIP SDK cannot yet do however, and since a very small number of users need/want access to these things, it will take some time for the SDK team to address those needs and implement new features with long term support. Meanwhile, with some investigation and persuasion, we can occasionally overcome these limitations using API's that were never intended to be used by the public.
10 |
11 | You will find a small handful of functions where certain interfaces are used that are unintuitive and lack documentation in the online MIP SDK docs. These functions will be clearly documented in the help comments, and absolutely no support is promised when the unsupported interfaces used by these functions changes between versions and backwards/forwards compatibility is lost or worse.
12 |
13 | ## Get-VmsCameraDiskUsage
14 |
15 | One common request that is currently unfulfilled by MIP SDK is the ability to retrieve the amount of storage used on a per-camera basis. It's possible to pull the space used/available for a live or archive storage location, but not on a camera by camera basis.
16 |
17 | I hear you - the information is in the Management Client right? So why can't we get it with the SDK? Doesn't the Management Client use the SDK? Well, no actually! A lot of the Management Client makes use of internal interfaces where developers have the flexibility to add/change capabilities between versions while keeping the MIP SDK interfaces more stable over time. A lot of what is done in the Management Client is irrelevant to most MIP SDK integration developers anyway.
18 |
19 | Long story short, this function uses a WCF client interface that is not supported for external use. The method of using this interface is very similar to the other _supported_ interfaces like the IConfigurationService for example and technically anyone who knows the web URL for the service can use it. The authentication for the interface is the same as any other Milestone WCF interface and this function transparently authenticates and uses this API on your behalf. Here's what the output looks like. The UsedSpace values are in bytes, and just ignore the AvailableSpace values for now. It seems like there may be some sort of overflow error where these values don't make a lot of sense - the field is probably not used in Management Client. If you need to know the AvailableSpace you should navigate down to the storage/archive objects under `$RecordingServer.StorageFolder.Storages[$i]` or `$RecordingServer.StorageFolder.Storages[$i].ArchiveStorageFolder.ArchiveStorages[$j]`.
20 |
21 | ```powershell
22 | PS C:\> Get-Hardware | Get-Camera | Get-VmsCameraDiskUsage | Format-Table *
23 |
24 | CameraId StorageId RecorderId UsedSpace AvailableSpace IsOnline
25 | -------- --------- ---------- --------- -------------- --------
26 | 3c25ff7a-7c76-49bd-bda0-116f5e051e48 cce0ef0f-36b1-4221-964d-e5de1f641741 72080191-d39d-4229-b151-65bcd740c393 4513 157586161664 True
27 | afa846f8-846c-4932-b206-c1c6c24e0b5f def84b4a-1e7a-4f99-ac5f-671ae76d520b 72080191-d39d-4229-b151-65bcd740c393 4519 157586161664 True
28 | c2733741-0c71-4197-89d2-030339c7a9ea def84b4a-1e7a-4f99-ac5f-671ae76d520b 72080191-d39d-4229-b151-65bcd740c393 161278294 157586161664 True
29 | c2733741-0c71-4197-89d2-030339c7a9ea e96f206b-3ecd-421a-906b-e32393b4bedb 72080191-d39d-4229-b151-65bcd740c393 705524466 12260420734976 True
30 | c2733741-0c71-4197-89d2-030339c7a9ea 2358075f-2291-4c43-86c8-9d6351f2ed59 72080191-d39d-4229-b151-65bcd740c393 71165280 197973835776 True
31 |
32 | PS C:\>
33 | ```
34 |
--------------------------------------------------------------------------------
/Samples/Rules/Get-VmsRule.ps1:
--------------------------------------------------------------------------------
1 | function Get-VmsRule {
2 | <#
3 | .SYNOPSIS
4 | Gets all VMS Rules available through Milestone's Configuration API.
5 | .DESCRIPTION
6 | Version 2020 R1 introduced support for rules in the Configuration API. This function is an example of how the Configuration API
7 | function Get-ConfigurationItem can be used to retrieve all the rules from the /RuleFolder configuration api path.
8 | .EXAMPLE
9 | PS C:\> Get-VmsRule -Name Default* | Select DisplayName, Path
10 | Gets all default rules from the VMS and displays only the name and path of the rule objects
11 | .EXAMPLE
12 | PS C:\> Get-VmsRule -Name 'Test Rule #1' | Select DisplayName, Path
13 | Gets a single rule named 'Test Rule #1' or emits an error if this rule does not exist since no wildcard is present in the Name parameter.
14 | .PARAMETER Name
15 | Specifies the display name, or partial name of the rule(s). Wildcards are supported and the default is to return all rules.
16 | .NOTES
17 | Support for rules was introduced in version 2020 R1, and early support for rules is quite limited. Check the MIP SDK Configuration API documentation for more information about rules.
18 | #>
19 | [CmdletBinding()]
20 | param(
21 | [Parameter(ValueFromPipelineByPropertyName)]
22 | [Alias('DisplayName')]
23 | [string]
24 | $Name = '*'
25 | )
26 |
27 | begin {
28 | $ms = Get-VmsManagementServer -ErrorAction Ignore
29 | if ($null -eq $ms) {
30 | throw "You must be connected to a Management Server. Use Connect-ManagementServer and then try again."
31 | }
32 | if ([version]$ms.Version -lt [version]'20.1') {
33 | throw "Support for rules in Milestone's Configuration API was not added until version 2020 R1. You must upgrade the Management Server to use this function."
34 | }
35 | }
36 |
37 | process {
38 | $matchingRuleCount = 0
39 | Get-ConfigurationItem -Path /RuleFolder -ChildItems | Where-Object DisplayName -like $Name | Foreach-Object {
40 | $matchingRuleCount++
41 | Write-Output $_
42 | }
43 |
44 | if ($matchingRuleCount -lt 1 -and ![wildcardpattern]::ContainsWildcardCharacters($Name)) {
45 | Write-Error "Rule not found matching name '$Name'"
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/Samples/Rules/README sp-MX.md:
--------------------------------------------------------------------------------
1 | # Trabajar con reglas
2 |
3 | Milestone introdujo soporte para trabajar con reglas en la versión 2020 R1, y el soporte se ha expandido en las versiones lanzadas desde entonces. Inicialmente había muchas acciones de regla que, si estaban presentes, evitarían que la regla se devolviera al solicitar elementos secundarios de /RuleFolder. En la versión 2020 R3 se admiten las acciones de reglas más comunes y la funcionalidad es mucho más utilizable.
4 |
5 | Dicho esto, actualmente no hay funciones/cmdlets integrados en MilestonePSTools para manipular reglas desde PowerShell. Parte de esto se debe al tiempo limitado disponible para extender MilestonePSTools, la otra parte es el desafío de diseñar funciones simples para trabajar con un concepto e interfaz complejos.
6 |
7 | Las secuencias de comandos de esta carpeta son ejemplos funcionales de cómo puede trabajar con reglas y se agregarán más adelante a medida que el tiempo lo permita. Estas funciones pueden llegar al módulo MilestonePSTools, o tal vez se creará un submódulo como "MilestonePSTools.Rules", ya que cualquier función desarrollada para trabajar con reglas son efectivamente solo arreglos creativos de las funciones Get-ConfigurationItem, Invoke-Item y Set-ConfigurationItem que a su vez envuelven el proxy WCF IConfigurationService. Es posible que tenga una opinión diferente sobre cómo se podría implementar la compatibilidad con las reglas en PowerShell, y al mantener nuestras versiones de opinión fuera del espacio de nombres del módulo MilestonePSTools, puede ser más fácil para usted hacer las cosas a su manera sin preocuparse por las colisiones de nombres de funciones.
8 |
9 |
10 | ## Get-VmsRule
11 |
12 | Esta función es un contenedor muy simple alrededor de la función `Get-ConfigurationItem` existente, que es a su vez un contenedor alrededor del cliente WCF IConfigurationService que interactúa directamente con la API de configuración en el servidor de gestión. He agregado el prefijo Vms ya que existe una buena posibilidad de que `Get-Rule` pueda colisionar con cualquier número de otros módulos (piense en firewalls, antivirus, etc.).
13 |
14 | Admite comodines y tiene un aspecto similar al de esto cuando se importa a la sesión de PowerShell y se llama a un conjunto predeterminado de reglas de VMS.
15 |
16 | ```powershell
17 | PS C:\> Get-VmsRule | select DisplayName, ItemType, Path, @{ Name = 'Enabled'; Expression = { $_.EnableProperty.Enabled } }
18 |
19 | DisplayName ItemType Path Enabled
20 | ----------- -------- ---- -------
21 | Default Start Audio Feed Rule Rule Rule[162fdb73-e0dc-4a2d-baa6-54b0d2b16684] True
22 | Default Record on Motion Rule Rule Rule[3307c095-a170-49d3-ab11-1baf8783acb9] True
23 | Default Record on Bookmark Rule Rule Rule[4ce46d3e-c4c7-46b2-a580-fc98cdc24611] True
24 | Default Goto Preset when PTZ is done Rule Rule Rule[7aa28f82-f3ff-4398-9781-061299178c7f] False
25 | Default Start Feed Rule Rule Rule[e34e9353-e6f5-43ff-8e8f-d4a558159b2b] True
26 | Default Record on Request Rule Rule Rule[fa2f8209-8d9b-4580-a6eb-17e58c99a610] True
27 | Default Start Metadata Feed Rule Rule Rule[fe61841f-544e-44d7-b7a8-cd709195162d] True
28 |
29 |
30 |
31 | PS C:\>
32 | ```
33 |
34 | ## Remove-VmsRule
35 |
36 | Esta es una función un poco más compleja que Get-VmsRule, pero sigue siendo un contenedor relativamente simple alrededor de las funciones Get-ConfigurationItem e Invoke-Method que utilizan la API de configuración para modificar la configuración de VMS. Esto es lo que se vería al eliminar la “Regla de fuente de audio de inicio predeterminada” usando comodines en el nombre de la regla (si realmente desea realizar la eliminación, puede omitir el switch WhatIf).
37 |
38 | ```powershell
39 | PS C:\> Remove-VmsRule -Name 'Default*Audio*' -WhatIf
40 | What if: Performing the operation "Remove Rule" on target "Default Start Audio Feed Rule".
41 |
42 | PS C:\>
43 | ```
44 |
--------------------------------------------------------------------------------
/Samples/Rules/README.md:
--------------------------------------------------------------------------------
1 | # Working with Rules
2 |
3 | Milestone introduced support for working with rules in version 2020 R1, and that support has expanded in the versions released since then. Initially there were many rule actions that, if present, would prevent the rule from being returned when requesting child items from /RuleFolder. In version 2020 R3 the most common rule actions are supported and the functionality is much more usable.
4 |
5 | That said - there are currently no functions/cmdlets built-in to MilestonePSTools for manipulating rules from PowerShell. Part of this is limited time available to spend on extending MilestonePSTools, and another is the challenge of designing simple functions to work with a fairly complex concept and interface.
6 |
7 | The scripts in this folder are functional examples of how you can work with rules, and more will be added later as time permits. These functions may make their way into the MilestonePSTools module, or perhaps a submodule will be created like "MilestonePSTools.Rules" since any functions developed for working with rules are effectively just creative arrangements of the Get-ConfigurationItem, Invoke-Item, and Set-ConfigurationItem functions which in turn are wrapping the IConfigurationService WCF proxy. You may have a different opinion on how support for rules in PowerShell should be implemented, and by keeping our opinionated versions out of the MilestonePSTools module namespace, it would be easier for you to do things your way without worrying about function name collisions.
8 |
9 | ## Get-VmsRule
10 |
11 | This function is a really simple wrapper around the existing `Get-ConfigurationItem` function which is itself a wrapper around the IConfigurationService WCF client which interfaces directly with the Configuration API on the Management Server. I added the Vms prefix since there's a good chance `Get-Rule` could collide with any number of other modules (think firewalls, antivirus, etc).
12 |
13 | It supports wildcards and looks something like this when imported into your PowerShell session and called against a default set of VMS rules.
14 |
15 | ```powershell
16 | PS C:\> Get-VmsRule | select DisplayName, ItemType, Path, @{ Name = 'Enabled'; Expression = { $_.EnableProperty.Enabled } }
17 |
18 | DisplayName ItemType Path Enabled
19 | ----------- -------- ---- -------
20 | Default Start Audio Feed Rule Rule Rule[162fdb73-e0dc-4a2d-baa6-54b0d2b16684] True
21 | Default Record on Motion Rule Rule Rule[3307c095-a170-49d3-ab11-1baf8783acb9] True
22 | Default Record on Bookmark Rule Rule Rule[4ce46d3e-c4c7-46b2-a580-fc98cdc24611] True
23 | Default Goto Preset when PTZ is done Rule Rule Rule[7aa28f82-f3ff-4398-9781-061299178c7f] False
24 | Default Start Feed Rule Rule Rule[e34e9353-e6f5-43ff-8e8f-d4a558159b2b] True
25 | Default Record on Request Rule Rule Rule[fa2f8209-8d9b-4580-a6eb-17e58c99a610] True
26 | Default Start Metadata Feed Rule Rule Rule[fe61841f-544e-44d7-b7a8-cd709195162d] True
27 |
28 |
29 |
30 | PS C:\>
31 | ```
32 |
33 | ## Remove-VmsRule
34 |
35 | This is a slightly more complex function than Get-VmsRule, yet still a relatively simple wrapper around the Get-ConfigurationItem, and Invoke-Method functions which use the Configuration API to modify the VMS configuration. Here's what it would look like to remove the 'Default Start Audio Feed Rule' using wildcards in the rule name. If you wanted to actually perform the removal, you can omit the -WhatIf switch.
36 |
37 | ```powershell
38 | PS C:\> Remove-VmsRule -Name 'Default*Audio*' -WhatIf
39 | What if: Performing the operation "Remove Rule" on target "Default Start Audio Feed Rule".
40 |
41 | PS C:\>
42 | ```
43 |
--------------------------------------------------------------------------------
/Samples/Rules/Remove-VmsRule.ps1:
--------------------------------------------------------------------------------
1 | function Remove-VmsRule {
2 | <#
3 | .SYNOPSIS
4 | Removes all VMS Rules with DisplayName's matching the provided name
5 | .DESCRIPTION
6 | Version 2020 R1 introduced support for rules in the Configuration API. This function is an example of how the Configuration API
7 | functions can be used to retrieve all the rules from the /RuleFolder configuration api path, and remove the matching rules by
8 | using Invoke-Method to call a Configuration API method available on the /RuleFolder object.
9 |
10 | This advanced function supports "ShouldProcess" so you may use the -WhatIf parameter to see what would happen if the -WhatIf switch
11 | were omitted.
12 | .EXAMPLE
13 | PS C:\> Remove-VmsRule -Name 'Test Rule #1' -WhatIf
14 | Shows what would happen if you used the same command without the -WhatIf switch
15 | .EXAMPLE
16 | PS C:\> Remove-VmsRule -Name 'Test Rule #1'
17 | Removes the 'Test Rule #1' rule from the Milestone VMS or emits an error if the rule could not be found.
18 | .PARAMETER Name
19 | Specifies the display name, or partial name of the rule(s). Wildcards are supported and the default is to return all rules.
20 | .NOTES
21 | Support for rules was introduced in version 2020 R1, and early support for rules is quite limited. Check the MIP SDK Configuration API documentation for more information about rules.
22 | #>
23 | [CmdletBinding(SupportsShouldProcess)]
24 | param(
25 | [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
26 | [Alias('DisplayName')]
27 | [ValidateNotNullOrEmpty()]
28 | [string]
29 | $Name
30 | )
31 |
32 | begin {
33 | $ms = Get-VmsManagementServer -ErrorAction Ignore
34 | if ($null -eq $ms) {
35 | throw "You must be connected to a Management Server. Use Connect-ManagementServer and then try again."
36 | }
37 | if ([version]$ms.Version -lt [version]'20.1') {
38 | throw "Support for rules in Milestone's Configuration API was not added until version 2020 R1. You must upgrade the Management Server to use this function."
39 | }
40 | }
41 |
42 | process {
43 | $ruleFolder = Get-ConfigurationItem -Path /RuleFolder -Recurse
44 | $rules = [array]($ruleFolder.Children | Where-Object DisplayName -like $Name)
45 | if ($rules.Count -lt 1 -and ![wildcardpattern]::ContainsWildcardCharacters($Name)) {
46 | Write-Error "Rule not found matching name '$Name'"
47 | return
48 | }
49 | foreach ($rule in $rules) {
50 | if ($PSCmdlet.ShouldProcess($rule.DisplayName, 'Remove Rule')) {
51 | $invokeInfo = $ruleFolder | Invoke-Method -MethodId RemoveRule
52 | $invokeInfo | Set-ConfigurationItemProperty -Key 'RemoveRulePath' -Value $rule.Path
53 | $invokeInfo | Invoke-Method -MethodId RemoveRule
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/Samples/Scheduled-Video-Export/README sp-MX.md:
--------------------------------------------------------------------------------
1 | ## Exportación de video programada
2 |
3 |
4 |
5 |
6 |
7 | En este ejemplo usaremos el cmdlet Register-ScheduledJob incluido en PowerShell para crear una tarea programada en Windows que ejecute nuestro script de exportación todos los días a medianoche.
8 |
9 | El siguiente ejemplo se puede copiar y pegar en una instancia elevada de PowerShell o PowerShell ISE. Asegúrese de ejecutar PowerShell como administrador; de lo contrario, Register-ScheduledJob producirá un error.
10 |
11 | La secuencia de comandos recopilará la dirección y las credenciales de VMS, y le pedirá una palabra clave de selección de cámara. Si desea exportar todas las cámaras con la palabra "Ascensor" en el nombre, puede ingresarla como palabra clave. La palabra clave no distingue entre mayúsculas y minúsculas. A continuación, se le pedirá una ruta de destino para guardar las exportaciones. Cada exportación se almacenará aquí en una subcarpeta con la fecha marcada. Antes de registrar el trabajo programado, se validarán las credenciales y la selección de la cámara. Si no se puede realizar una conexión o no hay cámaras que coincidan con la palabra clave proporcionada, no se creará el trabajo programado.
12 |
13 | El bit $expandingString puede parecer algo extraño. Lo que estamos haciendo aquí es crear un bloque de secuencia de comandos donde las variables específicas se expanden en sus valores reales antes de almacenarse en el trabajo programado. Esto nos permite pasar las respuestas desde la parte superior de la secuencia de comandos al cuerpo del scriptblock llamado por el trabajo programado.
14 |
15 | Al hacerlo de esta manera, podemos pedirle sus credenciales de VMS sin exponer su contraseña o tener que conservarla en el disco en forma cifrada. Su contraseña se recopila como una cadena segura y, a continuación, se convierte en una cadena larga que solo puede descifrar el usuario de Windows con el que está ejecutando la secuencia de comandos.
16 |
17 |
18 | ```powershell
19 | $InformationPreference = 'Continue'
20 | $server = Read-Host -Prompt "Server Address"
21 | $username = Read-Host -Prompt "Username"
22 | $password = Read-Host -Prompt "Password" -AsSecureString | ConvertFrom-SecureString
23 |
24 | do {
25 | $isBasic = Read-Host -Prompt "Basic user? (y/n)"
26 | } while ('y', 'n' -notcontains $isBasic)
27 |
28 | $keyword = Read-Host -Prompt "Keyword for camera selection"
29 |
30 | do {
31 | $destination = Read-Host -Prompt "Export path"
32 | } while (-not (Test-Path -Path $destination))
33 |
34 | try {
35 | Write-Information "Validating credentials and camera selection before we register the scheduled job"
36 | Write-Information "Connecting to $server as $username"
37 | $connected = $false
38 | Connect-ManagementServer -Server $server -Credential ([pscredential]::new($username, ($password | ConvertTo-SecureString))) -BasicUser:($isBasic -eq 'y')
39 | Write-Information "Connected"
40 | $connected = $true
41 |
42 | Write-Information "Verifying there is at least one camera with a name matching keyword '$keyword'"
43 | $cameras = Get-Hardware | Where-Object Enabled | Get-Camera | Where-Object { $_.Enabled -and $_.Name -like "*$keyword*" }
44 | if ($null -eq $cameras) {
45 | throw "No cameras found matching keyword '$keyword'"
46 | }
47 | else {
48 | Write-Information "Identified $($cameras.Count) cameras matching keyword '$keyword'"
49 | }
50 | }
51 | catch {
52 | throw
53 | }
54 | finally {
55 | if ($connected) {
56 | Disconnect-ManagementServer
57 | }
58 | }
59 |
60 |
61 | $expandingString = "
62 | `$pass = '$password' | ConvertTo-SecureString
63 | `$cred = [pscredential]::new('$username', `$pass)
64 | `$isBasic = '$isBasic' -eq 'y'
65 | Connect-ManagementServer -Server $server -Credential `$cred -BasicUser:`$isBasic
66 | `$cameras = Get-Hardware | Where-Object Enabled | Get-Camera | Where-Object { `$_.Enabled -and `$_.Name -like '*$keyword*' }
67 |
68 | `$start = (Get-Date -Hour 13 -Minute 0 -Second 0).AddDays(-1)
69 | `$end = (Get-Date -Hour 15 -Minute 0 -Second 0).AddDays(-1)
70 |
71 | Start-Export -CameraIds `$cameras.Id -StartTime `$start -EndTime `$end -Format DB -Path ""$destination\`$(`$start.ToString('yyyy-MM-dd'))""
72 |
73 | Disconnect-ManagementServer
74 | "
75 |
76 | $script = [scriptblock]::Create($expandingString)
77 | $trigger = New-JobTrigger -Daily -DaysInterval 1 -At (Get-Date -Hour 0 -Minute 0)
78 | Register-ScheduledJob -Name "Automated Export Example" -ScriptBlock $script -Trigger $trigger
79 | ```
80 |
--------------------------------------------------------------------------------
/Samples/Scheduled-Video-Export/README.md:
--------------------------------------------------------------------------------
1 | ## Scheduled Video Export
2 |
3 |
4 |
5 |
6 |
7 | In this sample we will use the Register-ScheduledJob cmdlet included in PowerShell to create a
8 | scheduled task in Windows which runs our export script every day at midnight.
9 |
10 | The sample below can be copied and pasted into an elevated PowerShell or PowerShell ISE instance.
11 | Make sure to run PowerShell as Administrator, otherwise the Register-ScheduledJob will throw an
12 | error.
13 |
14 | The script will collect the VMS address, credentials, and ask you for a camera selection keyword.
15 | If you want to export all cameras with the word "Elevator" in the name, you can enter that as a
16 | keyword. They keyword is case-insensitive. You will then be asked for a destination path to save
17 | exports. Each export will be stored in a date-stamped subfolder here. And before we register the
18 | scheduled job, the credentials and camera selection will be validated. If a connection cannot be
19 | made or there are no cameras matching the provided keyword, the scheduled job will not be created.
20 |
21 | The $expandingString bit may look somewhat foreign. What we're doing here is creating a script
22 | block where specific variables are expanded into their actual values before being stored in the
23 | scheduled job. This is allowing us to pass the answers from the top of the script into the body
24 | of the scriptblock called by the scheduled job.
25 |
26 | By doing it this way, we're able to ask you for your VMS credentials without exposing your password
27 | or having to persist it to disk in encrypted form. Your password is collected as a secure string,
28 | then converted to a long string that can only be decrypted by the Windows user with which you're
29 | executing the script.
30 |
31 | ```powershell
32 | $InformationPreference = 'Continue'
33 | $server = Read-Host -Prompt "Server Address"
34 | $username = Read-Host -Prompt "Username"
35 | $password = Read-Host -Prompt "Password" -AsSecureString | ConvertFrom-SecureString
36 |
37 | do {
38 | $isBasic = Read-Host -Prompt "Basic user? (y/n)"
39 | } while ('y', 'n' -notcontains $isBasic)
40 |
41 | $keyword = Read-Host -Prompt "Keyword for camera selection"
42 |
43 | do {
44 | $destination = Read-Host -Prompt "Export path"
45 | } while (-not (Test-Path -Path $destination))
46 |
47 | try {
48 | Write-Information "Validating credentials and camera selection before we register the scheduled job"
49 | Write-Information "Connecting to $server as $username"
50 | $connected = $false
51 | Connect-ManagementServer -Server $server -Credential ([pscredential]::new($username, ($password | ConvertTo-SecureString))) -BasicUser:($isBasic -eq 'y')
52 | Write-Information "Connected"
53 | $connected = $true
54 |
55 | Write-Information "Verifying there is at least one camera with a name matching keyword '$keyword'"
56 | $cameras = Get-Hardware | Where-Object Enabled | Get-Camera | Where-Object { $_.Enabled -and $_.Name -like "*$keyword*" }
57 | if ($null -eq $cameras) {
58 | throw "No cameras found matching keyword '$keyword'"
59 | }
60 | else {
61 | Write-Information "Identified $($cameras.Count) cameras matching keyword '$keyword'"
62 | }
63 | }
64 | catch {
65 | throw
66 | }
67 | finally {
68 | if ($connected) {
69 | Disconnect-ManagementServer
70 | }
71 | }
72 |
73 |
74 | $expandingString = "
75 | `$pass = '$password' | ConvertTo-SecureString
76 | `$cred = [pscredential]::new('$username', `$pass)
77 | `$isBasic = '$isBasic' -eq 'y'
78 | Connect-ManagementServer -Server $server -Credential `$cred -BasicUser:`$isBasic
79 | `$cameras = Get-Hardware | Where-Object Enabled | Get-Camera | Where-Object { `$_.Enabled -and `$_.Name -like '*$keyword*' }
80 |
81 | `$start = (Get-Date -Hour 13 -Minute 0 -Second 0).AddDays(-1)
82 | `$end = (Get-Date -Hour 15 -Minute 0 -Second 0).AddDays(-1)
83 |
84 | Start-Export -CameraIds `$cameras.Id -StartTime `$start -EndTime `$end -Format DB -Path ""$destination\`$(`$start.ToString('yyyy-MM-dd'))""
85 |
86 | Disconnect-ManagementServer
87 | "
88 |
89 | $script = [scriptblock]::Create($expandingString)
90 | $trigger = New-JobTrigger -Daily -DaysInterval 1 -At (Get-Date -Hour 0 -Minute 0)
91 | Register-ScheduledJob -Name "Automated Export Example" -ScriptBlock $script -Trigger $trigger
92 | ```
93 |
--------------------------------------------------------------------------------
/Samples/Scheduled-Video-Export/ScheduledVideoExport.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Samples/Scheduled-Video-Export/ScheduledVideoExport.png
--------------------------------------------------------------------------------
/Samples/ScheduledCameraReport/README sp-MX.md:
--------------------------------------------------------------------------------
1 | # Informe de cámara programado
2 |
3 |
4 |
5 | Hay muchas maneras de ejecutar secuencias de comandos/tareas programadas. En Windows, la forma más común es a través del Programador de tareas de Windows. Incluso existen varias formas de ejecutar secuencias de comandos de PowerShell en el Programador de tareas. Usted puede crear una tarea programada estándar a mano, o mediante algunos de los cmdlets de PowerShell en el módulo ScheduledTasks o, por último, mediante el módulo PSScheduledJob, que es la ruta que tomé para este ejemplo.
6 | Para utilizar este ejemplo, puede descargar ScheduledCameraReport.ps1 y colocarlo en una carpeta en el cliente o servidor de Windows donde desea que se encuentren los archivos de registro y los CSV de informes de cámara. Por ejemplo, puede colocarlo en `C:\scripts\ScheduledCameraReports\`. A continuación, abra PowerShell como administrador y ejecute la secuencia de comandos. ¡Eso es todo!
7 |
8 | ## Pero, ¿cómo funciona?
9 |
10 | La secuencia de comandos utiliza el nuevo parámetro de cambio “ShowDialog” en Connect-ManagementServer para darle la oportunidad de conectarse con éxito a su servidor de gestión. Una vez conectado, sus opciones de inicio de sesión se conservan en el disco en un archivo connection.xml en la misma carpeta. Si proporcionó una contraseña, se cifrará como una cadena segura automáticamente utilizando el ámbito “CurrentUser”. Esto significa que solo su cuenta de usuario puede descifrar esa contraseña.
11 |
12 | La tarea programada importará ese archivo connection.xml y colocará esos parámetros en el comando `Connect-ManagementServer`. Después de eso, ejecutamos el informe de la cámara y guardamos la salida en un archivo con marca de tiempo en la subcarpeta de informes que ahora debería existir en la carpeta en la que se encuentra su secuencia de comandos.
13 |
14 | Se creará un archivo de registro gracias al uso de `Start-Transcript` para darle una idea de cómo se ejecutó la tarea durante la última ejecución. El archivo se sobrescribirá cada vez para evitar que *eventualmente* llene su disco con registros. Asimismo, cualquier informe de la cámara con más de 30 días de duración se eliminará durante cada ejecución.
15 |
16 | ## Reflexiones adicionales
17 |
18 | El módulo PSScheduledJob es interesante por la forma en que funciona. Encontrará que la tarea programada ejecuta powershell.exe con un comando que carga la definición de trabajo programada desde `C:\Users\\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs`. Si sucede algo extraño y su archivo de registro de transcripción no lo muestra, puede encontrar más información en la subcarpeta `Salida` en la carpeta de definición de trabajo programada. Si la contraseña de usuario de Windows cambia, es posible que deba iniciar sesión en este equipo para asegurarse de que la secuencia de comandos todavía se está ejecutando, ya que se ejecuta bajo su contexto de usuario.
19 |
20 | Si el servidor de gestión está en un sistema diferente al de esta secuencia de comandos, es posible que deba agregar el parámetro `Credencial` a `Register-ScheduledJob` y proporcionar la credencial de usuario de Windows, a menos que decida usar la autenticación explícita de usuario de Windows o la autenticación de usuario básico y proporcione un nombre de usuario y una contraseña mientras ejecuta la secuencia de comandos para crear el trabajo o la tarea programados.
21 |
--------------------------------------------------------------------------------
/Samples/ScheduledCameraReport/README.md:
--------------------------------------------------------------------------------
1 | # Scheduled Camera Report
2 |
3 |
4 |
5 | There are are lot of ways to execute scripts/tasks on a schedule. In Windows, the most common way
6 | to do this is through the Windows Task Scheduler. And there are even multiple ways to execute
7 | PowerShell scripts in Task Scheduler! You can create a standard scheduled task by hand, or by using
8 | some of the PowerShell cmdlets in the ScheduledTasks module, or finally by using the PSScheduledJob
9 | module which is the route I went for this sample.
10 |
11 | To use this sample, you can download ScheduledCameraReport.ps1 and place it in some folder on your
12 | Windows client or server where you want your log file and camera report CSV's to live. For example,
13 | you could place it in `C:\scripts\ScheduledCameraReports\`. Then open PowerShell as Administrator
14 | and execute the script. That's it!
15 |
16 | ## But how does it work?
17 |
18 | The script uses the new "ShowDialog" switch parameter in Connect-ManagementServer to give you the
19 | opportunity to successfully connect to your Management Server. Once connected, your login choices
20 | get persisted to disk in a connection.xml file in the same folder. If you provided a password, it
21 | will be encrypted as a secure string automatically using "CurrentUser" scope. This means only your
22 | user account can decrypt that password.
23 |
24 | The scheduled task will import that connection.xml file and splat those parameters into the
25 | `Connect-ManagementServer` command. After that, we run the camera report and save the output to a
26 | timestamped file in the `reports` subfolder that should now exist in the folder your script is in.
27 |
28 | A log file will be created thanks to the use of `Start-Transcript` to give you an idea of how the
29 | task ran during the last execution. The file will be overwritten each time to avoid *eventually*
30 | filling your disk up with logs. Likewise, any camera reports older than 30 days will be deleted
31 | during each run.
32 |
33 | ## Additional thoughts
34 |
35 | The PSScheduledJob module is interesting in the way it works. You'll find the scheduled task runs
36 | powershell.exe with a command that loads your scheduled job definition from
37 | `C:\Users\\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs`. If something weird
38 | happens and your transcript log file doesn't show it, you might find more information in the
39 | `Output` subfolder under your scheduled job defintion folder. And if your Windows user password
40 | changes, you may need to be sure to login to this machine to make sure the script is still running
41 | since it runs under your user context.
42 |
43 | If your Management Server is on a different system than this script, you might need to add the
44 | `Credential` parameter to `Register-ScheduledJob` and provide your Windows user credential unless
45 | you decide to use explicit Windows user authentication or basic user authentication and provide
46 | both a username and password while running the script to create the scheduled job/task.
47 |
--------------------------------------------------------------------------------
/Samples/ScheduledCameraReport/ScheduledCameraReport.ps1:
--------------------------------------------------------------------------------
1 | #Requires -RunAsAdministrator
2 | #Requires -Modules MilestonePSTools, MipSdkRedist, PSScheduledJob
3 |
4 | # Collect & test Management Server login details and persist to disk
5 | Connect-ManagementServer -ShowDialog -Force -AcceptEula -ErrorAction Stop
6 | $loginSettings = Get-LoginSettings
7 | $authType = if ($loginSettings.IsBasicUser) { 'Basic' } else { 'Negotiate' }
8 | $credential = $loginSettings.CredentialCache.GetCredential($loginSettings.Uri, $authType)
9 | if ($credential.UserName -eq [string]::Empty) {
10 | $credential = $null
11 | }
12 | else {
13 | $credential = [pscredential]::new("$(if (![string]::IsNullOrWhiteSpace($credential.Domain)) {"$($credential.Domain)\"})$($credential.UserName)", $credential.SecurePassword)
14 | }
15 | $connectParams = @{
16 | ServerAddress = $loginSettings.Uri
17 | Credential = $credential
18 | BasicUser = $loginSettings.IsBasicUser
19 | SecureOnly = $loginSettings.SecureOnly
20 | AcceptEula = $true
21 | }
22 | $connectParams | Export-Clixml -Path $PSScriptRoot\connection.xml -Force
23 | Disconnect-ManagementServer
24 |
25 |
26 |
27 |
28 | # Setup scheduled job using PSScheduledJob module commands
29 | $jobOptions = New-ScheduledJobOption -RequireNetwork -MultipleInstancePolicy IgnoreNew
30 | $jobTrigger = New-JobTrigger -Daily -At (Get-Date -Hour 6 -Minute 0 -Second 0)
31 | $jobParams = @{
32 | Name = 'Daily Camera Report'
33 | ScheduledJobOption = $jobOptions
34 | Trigger = $jobTrigger
35 | RunNow = $true
36 | ArgumentList = $PSScriptRoot
37 | }
38 | Get-ScheduledJob -Name $jobParams.Name -ErrorAction Ignore | Unregister-ScheduledJob -Force -ErrorAction Stop
39 | Register-ScheduledJob @jobParams -ScriptBlock {
40 | param([string]$WorkingDirectory)
41 | try {
42 | # Transcript file is overwritten after every execution of the job so it always contains logs from the last run and does not grow indefinitely
43 | Start-Transcript -Path $WorkingDirectory\ScheduledCameraReport.log
44 | $reportsFolder = New-Item -Path (Join-Path $WorkingDirectory 'reports\') -ItemType Directory -Force | Select-Object -ExpandProperty FullName
45 | Write-Host 'Cleaning up camera reports older than 30 days'
46 | Get-ChildItem -Path $reportsFolder\Camera-Report_*.csv | Where-Object CreationTime -lt (Get-Date).AddDays(-30) | Remove-Item -Verbose
47 |
48 | # Import Management Server connection details from disk
49 | $connectParams = Import-Clixml -Path $WorkingDirectory\connection.xml
50 | Write-Host "Connecting to $($connectParams.ServerAddress). . ."
51 | Connect-ManagementServer @connectParams -Verbose
52 |
53 | $csvPath = Join-Path $reportsFolder "CameraReport_$(Get-Date -Format FileDateTime).csv"
54 | Write-Host "Running Get-CameraReport and saving results to $csvPath"
55 | Get-CameraReport -Verbose | Export-Csv -Path $csvPath -NoTypeInformation -Force
56 | }
57 | catch {
58 | Write-Error $_
59 | }
60 | finally {
61 | $loadedModules = Get-Module | Select-Object Name, Version | Out-String
62 | Write-Host "Loaded modules and versions: $loadedModules"
63 | Write-Host 'Finished. Disconnecting from Management Server.'
64 | Disconnect-ManagementServer
65 | Stop-Transcript
66 | }
67 | }
--------------------------------------------------------------------------------
/Samples/ScheduledCameraReport/ScheduledCameraReport_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Samples/ScheduledCameraReport/ScheduledCameraReport_screenshot.png
--------------------------------------------------------------------------------
/Samples/ScheduledLogExport/README sp-MX.md:
--------------------------------------------------------------------------------
1 | # ScheduledLogExport
2 |
3 |
4 |
5 | Hay muchas maneras de ejecutar secuencias de comandos/tareas programadas. En Windows, la forma más común de hacerlo es a través del Programador de tareas de Windows. Incluso existen varias formas de ejecutar secuencias de comandos de PowerShell en el Programador de tareas. Usted puede crear una tarea programada estándar a mano, o mediante algunos de los cmdlets de PowerShell en el módulo ScheduledTasks o, por último, mediante el módulo PSScheduledJob, que es la ruta que tomé para este ejemplo.
6 | Para utilizar este ejemplo, puede descargar ScheduledLogExport.ps1 y colocarlo en una carpeta en el cliente o servidor de Windows donde desea que se encuentren los archivos de registro y los CSV de exportación de registros. Por ejemplo, puede colocarlo en C:\scripts\ScheduledLogExport\. A continuación, abra PowerShell como administrador y ejecute la secuencia de comandos. ¡Eso es todo!
7 |
8 | ## Pero, ¿cómo funciona?
9 |
10 | La secuencia de comandos utiliza el nuevo parámetro de cambio “ShowDialog” en Connect-ManagementServer para darle la oportunidad de conectarse con éxito a su servidor de gestión. Una vez conectado, sus opciones de inicio de sesión se conservan en el disco en un archivo connection.xml en la misma carpeta. Si proporcionó una contraseña, se cifrará como una cadena segura automáticamente utilizando el ámbito “CurrentUser”. Esto significa que solo su cuenta de usuario puede descifrar esa contraseña.
11 |
12 | La tarea programada importará ese archivo connection.xml y colocará esos parámetros en el comando `Connect-ManagementServer`. Después de eso, ejecutamos la exportación de registros y guardamos la salida en un archivo con marca de tiempo en la subcarpeta de `exportaciones` que ahora debería existir en la carpeta en la que se encuentra su secuencia de comandos.
13 |
14 | Se creará un archivo de registro gracias al uso de `Start-Transcript` para darle una idea de cómo se ejecutó la tarea durante la última ejecución. El archivo se sobrescribirá cada vez para evitar que eventualmente llene su disco con registros. Asimismo, cualquier exportación con más de 30 días de duración se eliminará durante cada ejecución.
15 |
16 | ## En lugar de CSV
17 |
18 | Este ejemplo está diseñado para mostrar una forma en que puede automatizar la recuperación de registros. Muchas veces un archivo CSV no es el formato adecuado para los fines previstos. Si, en cambio, desea transformar y enviar estos datos a otra herramienta como Splunk, ElasticSearch, DataDog o simplemente otra base de datos SQL, puede modificar el ejemplo a partir de la línea 55 y, en lugar de canalizar los registros a CSV, puede optar por usar cualquier número de módulos de PowerShell para ayudar a conectarse al sistema de destino y enviar los registros allí. Cuando esté listo, vuelva a ejecutar la secuencia de comandos para eliminar el trabajo programado anterior y registre uno nuevo con las actualizaciones que haya agregado.
19 |
20 | ## Reflexiones adicionales
21 |
22 | El módulo PSScheduledJob es interesante por la forma en que funciona. Encontrará que la tarea programada ejecuta powershell.exe con un comando que carga la definición de trabajo programada desde `C:\Users\\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs`. Si sucede algo extraño y su archivo de registro de transcripción no lo muestra, puede encontrar más información en la subcarpeta Salida en la carpeta de definición de trabajo programada. Si la contraseña de usuario de Windows cambia, es posible que deba iniciar sesión en este equipo para asegurarse de que la secuencia de comandos todavía se está ejecutando, ya que se ejecuta bajo su contexto de usuario.
23 |
24 | Si el servidor de gestión está en un sistema diferente al de esta secuencia de comandos, es posible que deba agregar el parámetro `Credencial` a `Register-ScheduledJob` y proporcionar la credencial de usuario de Windows, a menos que decida usar la autenticación explícita de usuario de Windows o la autenticación de usuario básico y proporcione un nombre de usuario y una contraseña mientras ejecuta la secuencia de comandos para crear el trabajo o la tarea programados.
25 |
--------------------------------------------------------------------------------
/Samples/ScheduledLogExport/README.md:
--------------------------------------------------------------------------------
1 | # ScheduledLogExport
2 |
3 |
4 |
5 | There are are lot of ways to execute scripts/tasks on a schedule. In Windows, the most common way
6 | to do this is through the Windows Task Scheduler. And there are even multiple ways to execute
7 | PowerShell scripts in Task Scheduler! You can create a standard scheduled task by hand, or by using
8 | some of the PowerShell cmdlets in the ScheduledTasks module, or finally by using the PSScheduledJob
9 | module which is the route I went for this sample.
10 |
11 | To use this sample, you can download ScheduledLogExport.ps1 and place it in some folder on your
12 | Windows client or server where you want your log file and log export CSV's to live. For example,
13 | you could place it in `C:\scripts\ScheduledLogExport\`. Then open PowerShell as Administrator
14 | and execute the script. That's it!
15 |
16 | ## But how does it work?
17 |
18 | The script uses the new "ShowDialog" switch parameter in Connect-ManagementServer to give you the
19 | opportunity to successfully connect to your Management Server. Once connected, your login choices
20 | get persisted to disk in a connection.xml file in the same folder. If you provided a password, it
21 | will be encrypted as a secure string automatically using "CurrentUser" scope. This means only your
22 | user account can decrypt that password.
23 |
24 | The scheduled task will import that connection.xml file and splat those parameters into the
25 | `Connect-ManagementServer` command. After that, we run the log export and save the output to a
26 | timestamped file in the `exports` subfolder that should now exist in the folder your script is in.
27 |
28 | A log file will be created thanks to the use of `Start-Transcript` to give you an idea of how the
29 | task ran during the last execution. The file will be overwritten each time to avoid *eventually*
30 | filling your disk up with logs. Likewise, any exports older than 30 days will be deleted
31 | during each run.
32 |
33 | ## Instead of CSV
34 |
35 | This sample is intended to show one way you can automate log retrieval. Often times a CSV file is
36 | not the right format for your intended purposes. If you instead want to transform and send this
37 | data to another tool like Splunk, ElasticSearch, DataDog, or just another SQL database, you can
38 | modify the sample starting around line 55 and instead of piping the logs to CSV, you could choose
39 | to use any number of PowerShell modules to help connect to the destination system and send the logs
40 | there instead! When you're ready, re-run the script to remove the old scheduled job and register a
41 | new one with the updates you've added.
42 |
43 | ## Additional thoughts
44 |
45 | The PSScheduledJob module is interesting in the way it works. You'll find the scheduled task runs
46 | powershell.exe with a command that loads your scheduled job definition from
47 | `C:\Users\\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs`. If something weird
48 | happens and your transcript log file doesn't show it, you might find more information in the
49 | `Output` subfolder under your scheduled job defintion folder. And if your Windows user password
50 | changes, you may need to be sure to login to this machine to make sure the script is still running
51 | since it runs under your user context.
52 |
53 | If your Management Server is on a different system than this script, you might need to add the
54 | `Credential` parameter to `Register-ScheduledJob` and provide your Windows user credential unless
55 | you decide to use explicit Windows user authentication or basic user authentication and provide
56 | both a username and password while running the script to create the scheduled job/task.
57 |
--------------------------------------------------------------------------------
/Samples/ScheduledLogExport/ScheduledLogExport.ps1:
--------------------------------------------------------------------------------
1 | #Requires -RunAsAdministrator
2 | #Requires -Modules MilestonePSTools, MipSdkRedist, PSScheduledJob
3 |
4 | # Collect & test Management Server login details and persist to disk
5 | Connect-ManagementServer -ShowDialog -Force -AcceptEula -ErrorAction Stop
6 | $loginSettings = Get-LoginSettings
7 | $authType = if ($loginSettings.IsBasicUser) { 'Basic' } else { 'Negotiate' }
8 | $credential = $loginSettings.CredentialCache.GetCredential($loginSettings.Uri, $authType)
9 | if ($credential.UserName -eq [string]::Empty) {
10 | $credential = $null
11 | }
12 | else {
13 | $credential = [pscredential]::new("$(if (![string]::IsNullOrWhiteSpace($credential.Domain)) {"$($credential.Domain)\"})$($credential.UserName)", $credential.SecurePassword)
14 | }
15 | $connectParams = @{
16 | ServerAddress = $loginSettings.Uri
17 | Credential = $credential
18 | BasicUser = $loginSettings.IsBasicUser
19 | SecureOnly = $loginSettings.SecureOnly
20 | AcceptEula = $true
21 | }
22 | $connectParams | Export-Clixml -Path $PSScriptRoot\connection.xml -Force
23 | Disconnect-ManagementServer
24 |
25 |
26 |
27 |
28 | # Setup scheduled job using PSScheduledJob module commands
29 | $jobOptions = New-ScheduledJobOption -RequireNetwork -MultipleInstancePolicy IgnoreNew
30 | $jobTrigger = New-JobTrigger -Daily -At (Get-Date -Hour 6 -Minute 0 -Second 0)
31 | $jobParams = @{
32 | Name = 'Daily Log Export'
33 | ScheduledJobOption = $jobOptions
34 | Trigger = $jobTrigger
35 | RunNow = $true
36 | ArgumentList = $PSScriptRoot
37 | }
38 | Get-ScheduledJob -Name $jobParams.Name -ErrorAction Ignore | Unregister-ScheduledJob -Force -ErrorAction Stop
39 | Register-ScheduledJob @jobParams -ScriptBlock {
40 | param([string]$WorkingDirectory)
41 | try {
42 | # Transcript file is overwritten after every execution of the job so it always contains logs from the last run and does not grow indefinitely
43 | Start-Transcript -Path $WorkingDirectory\ScheduledLogExport.log
44 | $reportsFolder = New-Item -Path (Join-Path $WorkingDirectory 'exports\') -ItemType Directory -Force | Select-Object -ExpandProperty FullName
45 | Write-Host 'Cleaning up exports older than 30 days'
46 | Get-ChildItem -Path "$reportsFolder\Log-Export_*.csv" | Where-Object CreationTime -lt (Get-Date).AddDays(-30) | Remove-Item -Verbose
47 |
48 | # Import Management Server connection details from disk
49 | $connectParams = Import-Clixml -Path $WorkingDirectory\connection.xml
50 | Write-Host "Connecting to $($connectParams.ServerAddress). . ."
51 | Connect-ManagementServer @connectParams -Verbose
52 |
53 | $csvPath = Join-Path $reportsFolder "Log-Export_$(Get-Date -Format FileDateTime).csv"
54 | Write-Host "Running Get-Log and saving results to $csvPath"
55 | Get-Log -LogType Audit -BeginTime (Get-Date).Date.AddDays(-1) -EndTime (Get-Date).Date | Export-Csv -Path $csvPath -NoTypeInformation
56 | }
57 | catch {
58 | Write-Error $_
59 | }
60 | finally {
61 | $loadedModules = Get-Module | Select-Object Name, Version | Out-String
62 | Write-Host "Loaded modules and versions: $loadedModules"
63 | Write-Host 'Finished. Disconnecting from Management Server.'
64 | Disconnect-ManagementServer
65 | Stop-Transcript
66 | }
67 | }
--------------------------------------------------------------------------------
/Samples/ScheduledLogExport/ScheduledLogExport_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/Samples/ScheduledLogExport/ScheduledLogExport_screenshot.png
--------------------------------------------------------------------------------
/Samples/Set-AxisCameraSettings.ps1:
--------------------------------------------------------------------------------
1 | function Set-AxisCameraSettings
2 | {
3 | <#
4 | .SYNOPSIS
5 | Set Axis Zipstream settings, as well as frame rate
6 | .DESCRIPTION
7 | Quickly set some or all of the Axis Zipstream settings, as well as frame rate, on all streams or just the recorded or just the live
8 | .EXAMPLE
9 | Set-AxisCameraSettings -RecordingServerName * -StreamType Recorded -FPS 10 -ZipstreamCompression medium -ZipstreamFPSMode dynamic
10 |
11 | Sets the Record stream on all Axis cameras on all Recording Servers to 10 fps, Medium zipstream, and Dynamic FPS mode.
12 | .EXAMPLE
13 | Set-AxisCameraSettings -RecordingServerName "Milestone-RS" -StreamType LiveDefault -FPS 15 -ZipstreamGOPMode dynamic -ZipstreamMaxGOPLength 500
14 |
15 | Sets the Live stream on all Axis cameras on Recording Server "Milestone-RS" to 15fps, Dynamic GOP length, and a max GOP length of 500.
16 | .EXAMPLE
17 | Set-AxisCameraSettings -RecordingServerName * -StreamType All -FPS 8 -ZipstreamCompress extreme
18 |
19 | Sets all streams on all Axis cameras on all Recording Servers to 8fps and Extreme zipstream.
20 | #>
21 | [CmdletBinding()]
22 | param (
23 | [Parameter(Mandatory = $true)]
24 | $RecordingServerName,
25 | [Parameter(Mandatory = $true)]
26 | [ValidateSet("All","LiveDefault","Record")]
27 | $StreamType,
28 | [Parameter()]
29 | [ValidateRange(1,30)]
30 | $FPS,
31 | [Parameter()]
32 | [ValidateSet("off","low","medium","high","higher","extreme")]
33 | $ZipstreamCompression,
34 | [Parameter()]
35 | [ValidateSet("fixed","dynamic")]
36 | $ZipstreamFPSMode,
37 | [Parameter()]
38 | [ValidateSet("fixed","dynamic")]
39 | $ZipstreamGOPMode,
40 | [Parameter()]
41 | [ValidateRange(62,1200)]
42 | $ZipstreamMaxGOPLength
43 | )
44 |
45 | begin
46 | {
47 | if ($RecordingServerName -ne "*" -and (Get-RecordingServer).Name -notcontains $RecordingServerName)
48 | {
49 | Write-Host "Recording Server does not exist. Please check spelling and try again." -ForegroundColor Red
50 | Return
51 | }
52 | }
53 |
54 | process
55 | {
56 | foreach ($rec in Get-RecordingServer -Name $RecordingServerName)
57 | {
58 | Write-Progress -Activity "Recording Server $($rec.name)" -Id 1
59 | $hwProcessed = 1
60 | $allAxisHardware = $rec | Get-Hardware | Where-Object {$_.Enabled -and ($_ | Get-HardwareSetting).ProductID -like "*Axis*"}
61 | $hwQty = $allAxisHardware.Count
62 | foreach ($hardware in $allAxisHardware)
63 | {
64 | Write-Progress -Activity "Processing hardware $($hwProcessed) of $($hwQty)" -ParentId 1 -Id 2 -PercentComplete ($hwProcessed / $hwQty * 100)
65 | foreach ($camera in $hardware | Get-Camera | Where-Object Enabled)
66 | {
67 | $allStreams = $camera | Get-Stream -All
68 |
69 | if ($StreamType -eq "Record" -or $StreamType -eq "LiveDefault")
70 | {
71 | $streamNum = 0
72 | foreach ($stream in $allStreams)
73 | {
74 | if ($stream.$($StreamType) -ne $true)
75 | {
76 | $streamNum++
77 | } else
78 | {
79 | Break
80 | }
81 | }
82 |
83 | Set-AxisCameraSettingsHelper $camera $streamNum $FPS $ZipstreamCompression $ZipstreamFPSMode $ZipstreamGOPMode $ZipstreamMaxGOPLength
84 | }
85 |
86 | if ($StreamType -eq "All")
87 | {
88 | for ($streamNum = 0;$streamNum -lt $allStreams.Count;$streamNum++)
89 | {
90 | Set-AxisCameraSettingsHelper $camera $streamNum $FPS $ZipstreamCompression $ZipstreamFPSMode $ZipstreamGOPMode $ZipstreamMaxGOPLength
91 | }
92 | }
93 | }
94 | $hwProcessed++
95 | }
96 | }
97 | }
98 | }
99 |
100 | function Set-AxisCameraSettingsHelper ($camera,$streamNum,$FPS,$ZipstreamCompression,$ZipstreamFPSMode,$ZipstreamGOPMode,$ZipstreamMaxGOPLength)
101 | {
102 | <#
103 | .SYNOPSIS
104 | Don't run this script directly. It gets run by Set-AxisCameraSettings.
105 |
106 | .DESCRIPTION
107 | Don't run this script directly. It gets run by Set-AxisCameraSettings.
108 | #>
109 |
110 | $cameraSettings = $camera | Get-CameraSetting -Stream -StreamNumber $streamNum
111 |
112 | if ($null -ne $FPS -and $null -ne $cameraSettings.FPS)
113 | {
114 | $camera | Set-CameraSetting -Stream -StreamNumber $streamNum -Name FPS -Value $FPS
115 | }
116 |
117 | if ($null -ne $ZipstreamCompression -and $null -ne $cameraSettings.ZStrength)
118 | {
119 | $camera | Set-CameraSetting -Stream -StreamNumber $streamNum -Name ZStrength -Value $ZipstreamCompression
120 | }
121 |
122 | if ($null -ne $ZipstreamFPSMode -and $null -ne $cameraSettings.ZFpsMode)
123 | {
124 | $camera | Set-CameraSetting -Stream -StreamNumber $streamNum -Name ZFpsMode -Value $ZipstreamFPSMode
125 | }
126 |
127 | if ($null -ne $ZipstreamGOPMode -and $null -ne $cameraSettings.ZGopMode)
128 | {
129 | $camera | Set-CameraSetting -Stream -StreamNumber $streamNum -Name ZGopMode -Value $ZipstreamGOPMode
130 | }
131 |
132 | if ($null -ne $ZipstreamMaxGOPLength -and $null -ne $cameraSettings.ZGopLength)
133 | {
134 | $camera | Set-CameraSetting -Stream -StreamNumber $streamNum -Name ZGopLength -Value $ZipstreamMaxGOPLength
135 | }
136 | }
--------------------------------------------------------------------------------
/Samples/Set-HttpsEnabled.ps1:
--------------------------------------------------------------------------------
1 | function Set-HttpsEnabled {
2 | <#
3 | .SYNOPSIS
4 | Sets the HTTPSEnabled property for a hardware device to whatever value is valid for that device.
5 |
6 | .DESCRIPTION
7 | Each device driver publishes its own settings and validation parameters to the Management Server
8 | and values are case sensitive. Some drivers expect "Yes" and others expect "yes" for the same
9 | setting. This function demonstrates one way you could abstract this complication away into a
10 | function which will try to find the valid value name for you. All you have to do is supply the
11 | Enabled or Disabled switch when calling Set-HttpsEnabled.
12 |
13 | .PARAMETER Hardware
14 | Specifies the hardware device you will be making the change on. You can get this hardware object
15 | using Get-Hardware.
16 |
17 | .PARAMETER Enabled
18 | Specifies that the HTTPSEnabled setting should be enabled. The function will figure out it the
19 | right value is yes, enabled, true, or on, and whether those values should be capitalized or not.
20 |
21 | .PARAMETER Disabled
22 | Specifies that the HTTPSEnabled setting should be disabled. The function will figure out it the
23 | right value is no, disabled, false, or off, and whether those values should be capitalized or not.
24 |
25 | .PARAMETER SettingName
26 | Specifies the name of the HTTPSEnabled setting. Normally this is HTTPSEnabled, but just in case it
27 | is something else for another driver, you can specify the setting name here. Technically this means
28 | you could use this function to modify any "boolean" style setting.
29 |
30 | .EXAMPLE
31 | Get-Hardware | Set-HttpsEnabled -Enabled
32 |
33 | Sets the HTTPSEnabled value for all hardware devices to whatever the "enabled" value is.
34 | #>
35 | [CmdletBinding()]
36 | param(
37 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='Enabled')]
38 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='Disabled')]
39 | [VideoOS.Platform.ConfigurationItems.Hardware]
40 | $Hardware,
41 | [Parameter(Mandatory, ParameterSetName='Enabled')]
42 | [switch]
43 | $Enabled,
44 | [Parameter(Mandatory, ParameterSetName='Disabled')]
45 | [switch]
46 | $Disabled,
47 | [Parameter(ParameterSetName='Enabled')]
48 | [Parameter(ParameterSetName='Disabled')]
49 | [string]
50 | $SettingName = 'HTTPSEnabled'
51 | )
52 |
53 | process {
54 | $validValues = if ($Enabled) { @('yes', 'true', 'on', 'enabled') } else { @('no', 'false', 'off', 'disabled') }
55 | $infos = $Hardware | Get-HardwareSetting -Name $SettingName -ValueTypeInfo
56 | Write-Verbose "Valid values for $SettingName on $($Hardware.Model) are $(($infos | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name) -join ', ')"
57 | if ($null -eq $infos -or ($infos | Get-Member -MemberType NoteProperty).Count -eq 0) {
58 | Write-Error "ValueTypeInfo for $SettingName could not be found. Use Get-HardwareSetting to see a list of valid setting names."
59 | return
60 | }
61 |
62 | $valueName = ($infos | Get-Member -MemberType NoteProperty | Where-Object Name -in $validValues).Name
63 | if ($null -eq $valueName) {
64 | Write-Error "$SettingName has a value that is neither yes/no, true/false, or on/off."
65 | return
66 | }
67 | Write-Verbose "Setting $SettingName to $($infos.$valueName) on $($Hardware.Name) ($($Hardware.Id))"
68 | $Hardware | Set-HardwareSetting -Name $SettingName -Value $infos.$valueName
69 | }
70 | }
--------------------------------------------------------------------------------
/Samples/Set-MilestoneNames.ps1:
--------------------------------------------------------------------------------
1 | function Set-MilestoneNames
2 | {
3 | <#
4 | .SYNOPSIS
5 | Allows user to export enabled hardware and camera names to CSV. Once new names are added, allows user to import the updated names from the CSV.
6 |
7 | .DESCRIPTION
8 | Steps for use (if you are familiar with PowerShell functions, skip to step #):
9 | 1. Once opening Set-MilestoneNames.ps1 in Windows PowerShell ISE, click the green "Run Script" arrow (or just press F5)
10 | 2. Run the command in the first example below to Export the CSV. The path can be changed if desired.
11 | 3. If the machin this script is running on does not have Excel, copy the CSV file to a machine with Excel installed.
12 | 4. For any hardware or cameras where the name needs to be changed, enter the new name in the "NewHardwareName" or
13 | "NewCameraName" column. If the name doesn't need to be changed, don't do anything for that row.
14 | a. Do NOT change any of the other data.
15 | 5. Copy the CSV back to the machine this script is running on (if it was copied to another machine).
16 | 6. Run the command in the second example below to Import the new names. Make sure the path points to the CSV file.
17 |
18 | .EXAMPLE
19 | Set-MilestoneNames -Export -CsvPath "$($env:USERPROFILE)\Desktop\MilestoneNames.csv"
20 |
21 | Exports a CSV file named MilestoneNames.csv to the users desktop. This file contains all of the enabled hardware and camera named
22 | .EXAMPLE
23 | Set-MilestoneNames -Import -CsvPath "$($env:USERPROFILE)\Desktop\MilestoneNames.csv"
24 |
25 | Imports a CSV file that contains updated hardware or camera names.
26 | #>
27 |
28 | [CmdletBinding()]
29 | param (
30 | [Parameter(Mandatory=$false)]
31 | [switch]
32 | $Import,
33 | [Parameter(Mandatory=$false)]
34 | [switch]
35 | $Export,
36 | [Parameter(Mandatory=$true)]
37 | [string]
38 | $CsvPath
39 | )
40 |
41 | if ($Import -eq $false -and $Export -eq $false -or ($Import -eq $true -and $Export -eq $true))
42 | {
43 | Write-Host "Either -Import or -Export needs to be specified." -ForegroundColor Red
44 | Break
45 | }
46 |
47 | Connect-ManagementServer -ShowDialog -Force
48 | $data = New-Object System.Collections.Generic.List[PSCustomObject]
49 |
50 | if ($Export)
51 | {
52 | foreach ($hw in Get-VmsHardware | Where-Object Enabled)
53 | {
54 | $row = [PSCustomObject]@{
55 | Type = "Hardware"
56 | CurrentHardwareName = $hw.Name
57 | NewHardwareName = ""
58 | HardwareId = $hw.Id
59 | CurrentCameraName = ""
60 | NewCameraName = ""
61 | CameraId = ""
62 | }
63 | $data.Add($row)
64 |
65 | foreach ($cam in $hw | Get-VmsCamera)
66 | {
67 | $row = [PSCustomObject]@{
68 | Type = "Camera"
69 | CurrentHardwareName = ""
70 | NewHardwareName = ""
71 | HardwareId = ""
72 | CurrentCameraName = $cam.Name
73 | NewCameraName = ""
74 | CameraId = $cam.Id
75 | }
76 | $data.Add($row)
77 | }
78 | }
79 | $data | Export-Csv -Path $CsvPath -NoTypeInformation
80 | }
81 |
82 | if ($Import)
83 | {
84 | $csv = Import-Csv -Path $CsvPath
85 |
86 | foreach ($item in $csv)
87 | {
88 | if ($item.Type -eq "Hardware" -and -not [string]::IsNullOrEmpty($item.NewHardwareName))
89 | {
90 | Get-VmsHardware -HardwareId $item.HardwareID | Set-VmsHardware -Name $item.NewHardwareName
91 | }
92 |
93 | if ($item.Type -eq "Camera" -and -not [string]::IsNullOrEmpty($item.NewCameraName))
94 | {
95 | Get-VmsCamera -Id $item.CameraId | Set-VmsCamera -Name $item.NewCameraName
96 | }
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/Samples/Snapshots-On-Interval/README - sp-MX.md:
--------------------------------------------------------------------------------
1 | # Instantáneas en intervalos
2 |
3 | En este ejemplo se muestra cómo puede programar una tarea en Windows para recuperar instantáneas JPEG de cámaras seleccionadas en un intervalo determinado.
4 |
5 | Para probarlo, simplemente ejecute la secuencia de comandos setup.ps1 como administrador. La elevación es necesaria porque creará una tarea programada, pero la tarea en sí no requerirá ni se ejecutará con privilegios elevados.
6 | Asegúrese de ejecutar la secuencia de comandos desde un archivo en lugar de copiar y pegar la secuencia de comandos en una terminal de PowerShell. La secuencia de comandos utiliza la variable automática $PSScriptRoot para determinar el “directorio de trabajo” donde se almacenarán la configuración, el registro y las instantáneas. Por lo tanto, dondequiera que ejecute setup.ps1 será el directorio de trabajo para la tarea programada.
7 |
8 | Cuando ejecute la secuencia de comandos, se le pedirá la dirección y las credenciales del servidor de Milestone, luego ingresará el intervalo deseado entre instantáneas en segundos y seleccionará una o más cámaras para incluir a través de la GUI del “Selector de elementos” de Milestone.
9 |
10 | A continuación, guarde esta información en un archivo XML mediante Export-CliXml, lo que garantizará que las credenciales se cifren mediante la API de protección de datos de Windows (DPAPI) con el ámbito “CurrentUser”, lo que significa que solo el usuario de Windows actual podrá leer la credencial desde el disco.
11 |
12 | Por último, use Register-ScheduledJob para crear una tarea programada en Windows que encontrará en el Programador de tareas en Microsoft\/Windows\/PowerShell\/ScheduledJobs. La tarea programada se iniciará inmediatamente, así como en cada inicio de Windows, lea el archivo config.xml y use la información allí para iniciar sesión en el VMS de Milestone, luego comenzará a guardar instantáneas en el intervalo dado. La secuencia de comandos se ejecutará indefinidamente en un bucle infinito, y dormirá durante el tiempo adecuado entre la toma de instantáneas.
13 |
14 | Tenga en cuenta que si hay cientos de cámaras o más, este proceso podría llevar mucho tiempo y las instantáneas se tomarán en serie en lugar de en paralelo.
15 |
16 |
--------------------------------------------------------------------------------
/Samples/Snapshots-On-Interval/README.md:
--------------------------------------------------------------------------------
1 | # Snapshots On Interval
2 |
3 | This sample demonstrates how you can schedule a task in Windows to retrieve JPEG snapshots from select cameras on a given interval.
4 |
5 | To try it out, simply run the setup.ps1 script as Administrator. Elevation is required because you'll be creating a scheduled task,
6 | but the task itself will not require, or run with elevated privileges.
7 |
8 | Make sure to run the script from a file rather than copying and pasting the script into a PowerShell terminal. The script uses
9 | the $PSScriptRoot automatic variable to determine the "working directory" where the configuration, log, and snapshots will be stored.
10 | So wherever you run setup.ps1 from will be the working directory for the scheduled task.
11 |
12 | When you run the script, you will be prompted for Milestone server address and credentials, then you will enter the desired interval
13 | between snapshots in seconds, and you will select one or more cameras to be included via the Milestone "Item Picker" GUI.
14 |
15 | We then save this information to an XML file using Export-CliXml which will ensure the credentials are encrypted using the Windows
16 | Data Protection API (DPAPI) with "CurrentUser" scope meaning only the current windows user is able to read the credential from disk.
17 |
18 | Finally, we use Register-ScheduledJob to create a scheduled task in Windows which you'll find in Task Scheduler under
19 | Microsoft/Windows/PowerShell/ScheduledJobs. The scheduled task will start immediately, and also on every Windows startup, read the
20 | config.xml file and use the information there to login to the Milestone VMS, then begin saving snapshots on the given interval. The
21 | script will run indefinitely in a never-ending loop, sleeping for an appropriate time between taking snapshots.
22 |
23 | Note that if there are hundreds of cameras or more, this process could take a long time and the snapshots are being taken in serial
24 | fashion rather than in parallel.
25 |
--------------------------------------------------------------------------------
/Samples/Snapshots-On-Interval/setup.ps1:
--------------------------------------------------------------------------------
1 | #Requires -RunAsAdministrator
2 | #Requires -Modules 'MilestonePSTools'
3 |
4 | # The Requires statements ensure the user runs the script as Administrator, and has the MilestonePSTools module installed
5 |
6 | # Instead of setting -ErrorAction 'Stop' in every function call, we set ErrorActionPreference which applies to the whole script
7 | # This way, we stop the script if something goes wrong instead of proceeding with incomplete data
8 | $ErrorActionPreference = "Stop"
9 |
10 | # Present a login dialog and on successful login, store the properties we'll need to reconnect from the scheduled task in a hashtable.
11 | Connect-ManagementServer -ShowDialog -AcceptEula -Force
12 | $loginSettings = Get-LoginSettings
13 | $nc = $loginSettings.CredentialCache | Select-Object -First 1
14 | $config = @{
15 | Connection = @{
16 | ServerAddress = $loginSettings.Uri
17 | Credential = if ([string]::IsNullOrWhiteSpace($nc.UserName)) { $null } else { [pscredential]::new($nc.UserName, $nc.SecurePassword) }
18 | BasicUser = $loginSettings.IsBasicUser
19 | AcceptEula = $true
20 | }
21 | }
22 |
23 | # Get seconds between snapshots or the snapshot interval
24 | # Tests the user-input to make sure it's a valid integer before continuing
25 | while ($true) {
26 | $interval = Read-Host -Prompt "How many seconds between snapshots"
27 | $intValue = 0
28 | if ([int]::TryParse($interval, [ref] $intValue) -and $intValue -ge 1) {
29 | $config.IntervalSeconds = $intValue
30 | break
31 | }
32 | else {
33 | Write-Warning "Value must be an integer greater than 0. Please try again. . ."
34 | }
35 | }
36 |
37 | # Let the user select which cameras will be used. Note this gets the raw camera ids and even though
38 | # you can add cameras using a device group, only the cameras in that group at the time this script
39 | # is run will be included in the snapshot collection when the scheduled job is running.
40 | do {
41 | [array]$cameraIds = (Select-Camera -AllowFolders -AllowServers -RemoveDuplicates -OutputAsItem).FQID.ObjectId
42 | } while ($cameraIds.Count -le 0)
43 | $config.CameraIds = $cameraIds
44 |
45 | Disconnect-ManagementServer
46 |
47 |
48 | # Save the server address, credentials and settings to an XML file.
49 | # PowerShell encrypts the password in the credential so that only the current
50 | # user can actually decrypt it.
51 | $config | Export-Clixml -Path $PSScriptRoot\config.xml
52 |
53 | # Check if the scheduled job has previously been created, and delete it if so.
54 | $jobName = "Milestone Snapshot Task"
55 | Get-ScheduledJob -Name $jobName -ErrorAction Ignore | Unregister-ScheduledJob -Force
56 |
57 | # Create / Recreate the scheduled task with a trigger on system startup.
58 | $trigger = New-JobTrigger -AtStartup
59 |
60 | # If the job is already running and is triggered again, ignore the new instance. Require network as well
61 | $options = New-ScheduledJobOption -MultipleInstancePolicy IgnoreNew -RequireNetwork
62 |
63 | # Create the scheduled job. This appears in Task Scheduler in Windows under Microsoft/Windows/PowerShell/ScheduledJobs
64 | # We'll run the job immediately upon creation so that the task starts running in the background immediately
65 | $null = Register-ScheduledJob -Name $jobName -ScheduledJobOption $options -Trigger $trigger -RunNow -ArgumentList $PSScriptRoot -ScriptBlock {
66 | param([string]$WorkingDirectory)
67 | $ErrorActionPreference = "Stop"
68 | function Write-Log {
69 | <#
70 | .SYNOPSIS
71 | We want the transcript to include a timestamp for each line to make it easier to understand when
72 | and how long operations are taking. So all Write-Host commands will use Write-Log instead.
73 |
74 | Also, normally we'd use Write-Information but a bug in PowerShell means the transcript does not
75 | include anything from the Information stream. So Write-Host it is.
76 | #>
77 | param([string]$Message)
78 | Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff") - $Message"
79 | }
80 |
81 | try {
82 | Start-Transcript -Path (Join-Path -Path $WorkingDirectory -ChildPath job.log) -Force
83 |
84 | if (-not (Test-Path $WorkingDirectory\snapshots)) {
85 | Write-Log "Creating missing snapshots subfolder"
86 | $null = New-Item -Path $WorkingDirectory\snapshots -ItemType Directory -Force
87 | }
88 |
89 | Write-Log "Importing configuration"
90 | $config = Import-Clixml -Path $WorkingDirectory\config.xml
91 |
92 | $connected = $false
93 | Write-Log "Connecting to Management Server at $($config.Server) with user $($config.Credential.UserName). . ."
94 | $connection = $config.Connection
95 | Connect-ManagementServer @connection
96 | Write-Log "Connected"
97 | $connected = $true
98 |
99 | Write-Log "Retrieving snapshots every $($config.IntervalSeconds) seconds"
100 | $interval = New-TimeSpan -Seconds $config.IntervalSeconds
101 | $stopwatch = [diagnostics.stopwatch]::new()
102 | while ($true) {
103 | $stopwatch.Restart()
104 | foreach ($id in $config.CameraIds) {
105 | $camera = Get-VmsCamera -Id $id
106 | $folder = Join-Path -Path $WorkingDirectory -ChildPath snapshots\$($camera.Name)\
107 | if (-not (Test-Path -Path $folder)) {
108 | Write-Log "Creating subfolder for camera $($camera.Name) at $folder"
109 | $null = New-Item -Path $folder -ItemType Directory -Force
110 | }
111 | Write-Log "Getting snapshot from $($camera.Name)"
112 | $null = $camera | Get-Snapshot -Live -Save -Path $folder -UseFriendlyName
113 | }
114 |
115 | # Figure out how much we need to sleep in order to aim for the user-defined interval.
116 | # So subtract the time we spent generating snapshots from the desired interval, and sleep
117 | # for the difference. If the snapshots took longer than the interval time, then we'll just
118 | # skip the delay and repeat the process immediately
119 | $delay = $interval - $stopwatch.Elapsed
120 | if ($delay.TotalMilliseconds -gt 0) {
121 | Write-Log "Completed cycle in $($stopwatch.Elapsed.TotalSeconds) seconds. Sleeping for $($delay.TotalMilliseconds)ms"
122 | Start-Sleep -Milliseconds $delay.TotalMilliseconds
123 | }
124 | else {
125 | Write-Log "WARNING: Completed cycle in $($stopwatch.Elapsed.TotalSeconds) seconds. This is longer than the desired interval of $($interval.TotalSeconds)"
126 | }
127 | }
128 | }
129 | finally {
130 | if ($connected) {
131 | Write-Log "Logging out of Management Server"
132 | Disconnect-ManagementServer
133 | }
134 | Stop-Transcript
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Samples/Test-DataPresence/README - sp-MX.md:
--------------------------------------------------------------------------------
1 | ## Test Data Presence
2 |
3 | En este cmdlet (o grupo de cmdlets) probamos la presencia de datos entre un StartTime y EndTime determinados para dispositivos de cualquier tipo, incluidas cámaras, micrófonos, altavoces y metadatos.
4 |
5 | El método utilizado para comprobar la presencia de datos es recuperar una instantánea en el StartTime dado y comprobar el valor de la propiedad DateTime. Si está entre StartTime y EndTime, sabemos que hay al menos _algunos_ datos dentro del lapso de tiempo dado.
6 |
7 | Gracias al comportamiento y a las propiedades adicionales disponibles en los datos de reproducción, podemos determinar a partir de esa única instantánea si hay algún dato disponible en todo el lapso de tiempo. Ya sea que la instantánea sea para una cámara, micrófono, altavoz o metadatos.
8 |
9 | Al llamar a “GetNearest ([DateTime])” en una fuente de datos en MIP SDK, se devolverá un fotograma de datos si existe algo disponible y será algún tiempo antes o después del valor DateTime dado. Junto con los datos habrá valores que indican el valor DateTime del siguiente fotograma de datos disponible y el fotograma de datos anterior (si está disponible).
10 |
11 | Por lo tanto, si los datos devueltos son _anteriores_ a StartTime, y el valor de NextDateTime está entre StartTime y EndTime, podemos afirmar con seguridad que existen datos disponibles en ese lapso de tiempo.
12 |
13 | Y si el fotograma más cercano está _después_ de EndTime, sabemos que la imagen más cercana a StartTime es posterior de EndTime, por lo que no existen datos presentes entre StartTime y EndTime.
14 |
15 | El cmdlet Test-DataPresence acepta un elemento de configuración de cualquiera de los cuatro tipos de datos y pasa la solicitud a funciones más específicas como Test-VideoPresence, Test-AudioPresence o Test-MetadataPresence. Pero las tres funciones operan de manera muy similar, cada una haciendo uso de las clases de fuente de datos coincidentes del MIP SDK para consultar la base de datos multimedia.
16 |
17 | ```powershell
18 | $InformationPreference = 'Continue'
19 | $server = Read-Host -Prompt "Server Address"
20 | $credential = Get-Credential
21 |
22 | do {
23 | $isBasic = Read-Host -Prompt "Basic user? (y/n)"
24 | } while ('y', 'n' -notcontains $isBasic)
25 |
26 | try {
27 | Write-Information "Connecting to $server as $username"
28 | $connected = $false
29 | Connect-ManagementServer -Server $server -Credential $credential -BasicUser:($isBasic -eq 'y')
30 | Write-Information "Connected"
31 | $connected = $true
32 |
33 | foreach ($lock in Get-EvidenceLock) {
34 | $lockHasData = $false
35 | $cameras = $lock.DeviceIds | Foreach-Object { try { Get-Camera -Id $_ } catch { } }
36 | foreach ($camera in $cameras) {
37 | $dataExists = $camera | Test-DataPresence -StartTime $lock.StartTime -EndTime $lock.EndTime
38 | if ($dataExists) {
39 | $lockHasData = $true
40 | break;
41 | }
42 | }
43 |
44 | if (-not $lockHasData) {
45 | $lock
46 | }
47 | }
48 | }
49 | catch {
50 | throw
51 | }
52 | finally {
53 | if ($connected) {
54 | Disconnect-ManagementServer
55 | }
56 | }
57 | ```
58 |
--------------------------------------------------------------------------------
/Samples/Test-DataPresence/README.md:
--------------------------------------------------------------------------------
1 | ## Test Data Presence
2 |
3 | In this cmdlet (or group of cmdlets) we test for the presence of data between a given StartTime
4 | and EndTime for devices of any type including cameras, microphones, speakers and metadata.
5 |
6 | The method used to test for the presence of data is to retrieve a snapshot at the given StartTime
7 | and check the value of the DateTime property. If it's between StartTime and EndTime, we know that
8 | there's at least _some_ data within the given span of time.
9 |
10 | Thanks to the behavior, and additional properties available on playback data, we can determine
11 | from that single snapshot whether any data is available in the entire span of time. Whether the
12 | snapshot is for a camera, microphone, speaker, or metadata.
13 |
14 | When calling "GetNearest([DateTime])" on a data source in MIP SDK, a frame of data will be
15 | returned if anything is available, and it will be some time before or after the given DateTime
16 | value. Along with the data will be values indicating the DateTime value of the next available
17 | frame of data, and the previous frame of data (if available).
18 |
19 | So if the data returned is _before_ the StartTime, and the NextDateTime value is between
20 | StartTime and EndTime, then we can safely say there is data available in that time span.
21 |
22 | And if the nearest frame is _after_ the EndTime, we know that the nearest image to StartTime is
23 | after the EndTime and thus there is no data present between StartTime and EndTime.
24 |
25 | The Test-DataPresence cmdlet accepts a configuration item of any of the four data types, and
26 | passes the request on to more specific functions like Test-VideoPresence, Test-AudioPresence, or
27 | Test-MetadataPresence. But all three of these functions work very similarly - each making use of
28 | the matching data source classes from MIP SDK to query the media database.
29 |
30 | ```powershell
31 | $InformationPreference = 'Continue'
32 | $server = Read-Host -Prompt "Server Address"
33 | $credential = Get-Credential
34 |
35 | do {
36 | $isBasic = Read-Host -Prompt "Basic user? (y/n)"
37 | } while ('y', 'n' -notcontains $isBasic)
38 |
39 | try {
40 | Write-Information "Connecting to $server as $username"
41 | $connected = $false
42 | Connect-ManagementServer -Server $server -Credential $credential -BasicUser:($isBasic -eq 'y')
43 | Write-Information "Connected"
44 | $connected = $true
45 |
46 | foreach ($lock in Get-EvidenceLock) {
47 | $lockHasData = $false
48 | $cameras = $lock.DeviceIds | Foreach-Object { try { Get-Camera -Id $_ } catch { } }
49 | foreach ($camera in $cameras) {
50 | $dataExists = $camera | Test-DataPresence -StartTime $lock.StartTime -EndTime $lock.EndTime
51 | if ($dataExists) {
52 | $lockHasData = $true
53 | break;
54 | }
55 | }
56 |
57 | if (-not $lockHasData) {
58 | $lock
59 | }
60 | }
61 | }
62 | catch {
63 | throw
64 | }
65 | finally {
66 | if ($connected) {
67 | Disconnect-ManagementServer
68 | }
69 | }
70 | ```
71 |
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/images/logo.png
--------------------------------------------------------------------------------
/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MilestoneSystemsInc/PowerShellSamples/ecbb1d9db0c12efd0946d7d904cc63a49e36e827/images/screenshot.png
--------------------------------------------------------------------------------