RadioCodeCalculator.psm1
|
################################################################################ # # Radio Code Calculator Web API client for PowerShell. # Generate radio unlocking codes for various car radio players. # # Version : PowerShell SDK v1.0.0 # PowerShell : Windows PowerShell 5.1 / PowerShell 7+ # Author : Bartosz Wójcik (support@pelock.com) # Project : https://www.pelock.com/products/radio-code-calculator # Homepage : https://www.pelock.com # ################################################################################ Set-StrictMode -Version Latest if ($PSVersionTable.PSVersion.Major -lt 6) { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } $script:RadioApiUrl = 'https://www.pelock.com/api/radio-code-calculator/v1' $script:RadioUserAgent = 'PELock Radio Code Calculator' $script:RadioTimeoutSec = 3600 class RadioErrors { static [int] $ERROR_CONNECTION = -1 static [int] $SUCCESS = 0 static [int] $INVALID_INPUT = 1 static [int] $INVALID_COMMAND = 2 static [int] $INVALID_RADIO_MODEL = 3 static [int] $INVALID_SERIAL_LENGTH = 4 static [int] $INVALID_SERIAL_PATTERN = 5 static [int] $INVALID_SERIAL_NOT_SUPPORTED = 6 static [int] $INVALID_EXTRA_LENGTH = 7 static [int] $INVALID_EXTRA_PATTERN = 8 static [int] $INVALID_LICENSE = 100 } function ConvertTo-RadioRegex { param($Pattern) if ($null -eq $Pattern -or [string]::IsNullOrEmpty([string]$Pattern)) { return $null } $text = [string]$Pattern if ($text.Length -ge 2 -and $text[0] -eq [char]'/' -and $text.LastIndexOf('/') -gt 0) { $last = $text.LastIndexOf('/') return $text.Substring(1, $last - 1) } return $text } function ConvertTo-RadioFormBody { param([Parameter(Mandatory)] [hashtable]$Fields) $parts = foreach ($key in $Fields.Keys) { $name = [uri]::EscapeDataString([string]$key) $value = [uri]::EscapeDataString([string]$Fields[$key]) '{0}={1}' -f $name, $value } return ($parts -join '&') } function ConvertFrom-RadioResult { param($Result) if ($null -eq $Result -or $Result -eq $false) { return $null } if ($Result -is [hashtable]) { return $Result } $table = @{} foreach ($property in $Result.PSObject.Properties) { $table[$property.Name] = $property.Value } return $table } class RadioModel { [string] $name = '' [int] $serial_max_len = 0 hidden [hashtable] $_serial_regex_patterns = @{} [int] $extra_max_len = 0 hidden [hashtable] $_extra_regex_patterns = $null [string] $default_programming_language = 'php' RadioModel([string]$Name, [int]$SerialMaxLen, $SerialRegexPattern) { $this.Initialize($Name, $SerialMaxLen, $SerialRegexPattern, 0, $null) } RadioModel([string]$Name, [int]$SerialMaxLen, $SerialRegexPattern, [int]$ExtraMaxLen) { $this.Initialize($Name, $SerialMaxLen, $SerialRegexPattern, $ExtraMaxLen, $null) } RadioModel([string]$Name, [int]$SerialMaxLen, $SerialRegexPattern, [int]$ExtraMaxLen, $ExtraRegexPattern) { $this.Initialize($Name, $SerialMaxLen, $SerialRegexPattern, $ExtraMaxLen, $ExtraRegexPattern) } hidden [void] Initialize([string]$Name, [int]$SerialMaxLen, $SerialRegexPattern, [int]$ExtraMaxLen, $ExtraRegexPattern) { $this.name = $Name $this.serial_max_len = $SerialMaxLen $this._serial_regex_patterns = @{} $this.default_programming_language = 'php' if ($SerialRegexPattern -is [string]) { $this._serial_regex_patterns[$this.default_programming_language] = $SerialRegexPattern } elseif ($SerialRegexPattern -is [hashtable]) { $this._serial_regex_patterns = $SerialRegexPattern } elseif ($null -ne $SerialRegexPattern) { $map = @{} foreach ($property in $SerialRegexPattern.PSObject.Properties) { $map[$property.Name] = $property.Value } $this._serial_regex_patterns = $map } $this.extra_max_len = $ExtraMaxLen $this._extra_regex_patterns = $null if ($ExtraMaxLen -ne 0 -and $null -ne $ExtraRegexPattern) { if ($ExtraRegexPattern -is [string]) { $this._extra_regex_patterns = @{ $this.default_programming_language = $ExtraRegexPattern } } elseif ($ExtraRegexPattern -is [hashtable]) { $this._extra_regex_patterns = $ExtraRegexPattern } else { $map = @{} foreach ($property in $ExtraRegexPattern.PSObject.Properties) { $map[$property.Name] = $property.Value } $this._extra_regex_patterns = $map } } } [string] serial_regex_pattern() { if (-not $this._serial_regex_patterns.ContainsKey($this.default_programming_language)) { return '' } return [string]$this._serial_regex_patterns[$this.default_programming_language] } [string] extra_regex_pattern() { if ($null -eq $this._extra_regex_patterns) { return $null } if (-not $this._extra_regex_patterns.ContainsKey($this.default_programming_language)) { return $null } return [string]$this._extra_regex_patterns[$this.default_programming_language] } [int] validate([string]$Serial) { return $this.validate($Serial, $null) } [int] validate([string]$Serial, [string]$Extra) { if ($Serial.Length -ne $this.serial_max_len) { return [RadioErrors]::INVALID_SERIAL_LENGTH } $serialPattern = ConvertTo-RadioRegex -Pattern $this.serial_regex_pattern() if ($serialPattern -and $Serial -notmatch $serialPattern) { return [RadioErrors]::INVALID_SERIAL_PATTERN } if ($null -ne $Extra -and $Extra.Length -gt 0) { if ($Extra.Length -ne $this.extra_max_len) { return [RadioErrors]::INVALID_EXTRA_LENGTH } $extraPattern = ConvertTo-RadioRegex -Pattern $this.extra_regex_pattern() if ($extraPattern -and $Extra -notmatch $extraPattern) { return [RadioErrors]::INVALID_EXTRA_PATTERN } } return [RadioErrors]::SUCCESS } } class RadioModels { static [object] $RENAULT_DACIA static [object] $CHRYSLER_PANASONIC_TM9 static [object] $CHRYSLER_DODGE_VP static [object] $FORD_M_SERIES static [object] $FORD_V_SERIES static [object] $FORD_TRAVELPILOT static [object] $FIAT_STILO_BRAVO_VISTEON static [object] $FIAT_DAIICHI static [object] $FIAT_VP static [object] $TOYOTA_ERC static [object] $JEEP_CHEROKEE static [object] $NISSAN_GLOVE_BOX static [object] $ECLIPSE_ESN static [object] $JAGUAR_ALPINE static [RadioModel] Get([object]$RadioModelParams) { $arr = @($RadioModelParams) $extraLen = 0 $extraPat = $null if ($arr.Count -gt 3) { $extraLen = [int]$arr[3] } if ($arr.Count -gt 4) { $extraPat = $arr[4] } return [RadioModel]::new([string]$arr[0], [int]$arr[1], $arr[2], $extraLen, $extraPat) } } [RadioModels]::RENAULT_DACIA = @('renault-dacia', 4, '^([A-Z]{1}[0-9]{3})$') [RadioModels]::CHRYSLER_PANASONIC_TM9 = @('chrysler-panasonic-tm9', 4, '^([0-9]{4})$') [RadioModels]::CHRYSLER_DODGE_VP = @('chrysler-dodge-vp', 4, '^([a-zA-Z0-9]{4})$') [RadioModels]::FORD_M_SERIES = @('ford-m-series', 6, '^([0-9]{6})$') [RadioModels]::FORD_V_SERIES = @('ford-v-series', 6, '^([0-9]{6})$') [RadioModels]::FORD_TRAVELPILOT = @('ford-travelpilot', 7, '^([0-9]{7})$') [RadioModels]::FIAT_STILO_BRAVO_VISTEON = @('fiat-stilo-bravo-visteon', 6, '^([a-zA-Z0-9]{6})$') [RadioModels]::FIAT_DAIICHI = @('fiat-daiichi', 4, '^([0-9]{4})$') [RadioModels]::FIAT_VP = @('fiat-vp', 4, '^([0-9]{4})$') [RadioModels]::TOYOTA_ERC = @('toyota-erc', 16, '^([a-zA-Z0-9]{16})$') [RadioModels]::JEEP_CHEROKEE = @('jeep-cherokee', 14, '^([a-zA-Z0-9]{10}[0-9]{4})$') [RadioModels]::NISSAN_GLOVE_BOX = @('nissan-glove-box', 12, '^([a-zA-Z0-9]{12})$') [RadioModels]::ECLIPSE_ESN = @('toyota-erc', 6, '^([a-zA-Z0-9]{6})$') [RadioModels]::JAGUAR_ALPINE = @('jaguar-alpine', 5, '^([0-9]{5})$') class RadioCodeCalculator { static [string] $API_URL = 'https://www.pelock.com/api/radio-code-calculator/v1' [string] $_apiKey = $null RadioCodeCalculator() { $this._apiKey = $null } RadioCodeCalculator([string]$ApiKey) { $this._apiKey = $ApiKey } [object[]] Login() { $params = @{ command = 'login' } $result = $this.PostRequest($params) $errorCode = [RadioErrors]::ERROR_CONNECTION if ($result -and $result.ContainsKey('error')) { $errorCode = [int]$result['error'] } return @($errorCode, $result) } [object[]] Calc($RadioModel, [string]$RadioSerialNumber) { return $this.Calc($RadioModel, $RadioSerialNumber, '') } [object[]] Calc($RadioModel, [string]$RadioSerialNumber, [string]$RadioExtraData) { $modelName = $RadioModel if ($RadioModel -is [RadioModel]) { $modelName = $RadioModel.name } $params = @{ command = 'calc' radio_model = [string]$modelName serial = $RadioSerialNumber extra = $RadioExtraData } $result = $this.PostRequest($params) $errorCode = [RadioErrors]::ERROR_CONNECTION if ($result -and $result.ContainsKey('error')) { $errorCode = [int]$result['error'] } return @($errorCode, $result) } [object[]] Info($RadioModel) { $modelName = $RadioModel if ($RadioModel -is [RadioModel]) { $modelName = $RadioModel.name } $params = @{ command = 'info' radio_model = [string]$modelName } $result = $this.PostRequest($params) if ($null -eq $result -or [int]$result['error'] -ne [RadioErrors]::SUCCESS) { $errorCode = [RadioErrors]::ERROR_CONNECTION if ($result -and $result.ContainsKey('error')) { $errorCode = [int]$result['error'] } return @($errorCode, $null) } $extraMax = 0 $extraPat = $null if ($result.ContainsKey('extraMaxLen')) { $extraMax = [int]$result['extraMaxLen'] } if ($result.ContainsKey('extraRegexPattern')) { $extraPat = $result['extraRegexPattern'] } $model = [RadioModel]::new([string]$modelName, [int]$result['serialMaxLen'], $result['serialRegexPattern'], $extraMax, $extraPat) return @([int]$result['error'], $model) } [object[]] List() { $params = @{ command = 'list' } $result = $this.PostRequest($params) if ($null -eq $result -or [int]$result['error'] -ne [RadioErrors]::SUCCESS) { $errorCode = [RadioErrors]::ERROR_CONNECTION if ($result -and $result.ContainsKey('error')) { $errorCode = [int]$result['error'] } return @($errorCode, $null) } $radioModels = New-Object System.Collections.Generic.List[RadioModel] $supported = $result['supportedRadioModels'] foreach ($property in $supported.PSObject.Properties) { $radioModel = $property.Value $extraMax = 0 $extraPat = $null if ($radioModel.PSObject.Properties['extraMaxLen']) { $extraMax = [int]$radioModel.extraMaxLen } if ($radioModel.PSObject.Properties['extraRegexPattern']) { $extraPat = $radioModel.extraRegexPattern } $model = [RadioModel]::new($property.Name, [int]$radioModel.serialMaxLen, $radioModel.serialRegexPattern, $extraMax, $extraPat) $radioModels.Add($model) } return @([int]$result['error'], $radioModels.ToArray()) } [hashtable] PostRequest([hashtable]$ParamsArray) { if ($this._apiKey) { $ParamsArray['key'] = $this._apiKey } $defaultError = @{ error = [RadioErrors]::ERROR_CONNECTION } try { $body = ConvertTo-RadioFormBody -Fields $ParamsArray $headers = @{ 'User-Agent' = $script:RadioUserAgent } $result = Invoke-RestMethod -Uri $script:RadioApiUrl -Method Post -Body $body ` -ContentType 'application/x-www-form-urlencoded' -Headers $headers ` -TimeoutSec $script:RadioTimeoutSec } catch { return $defaultError } $table = ConvertFrom-RadioResult -Result $result if ($null -eq $table -or -not $table.ContainsKey('error')) { return $defaultError } return $table } [hashtable] post_request([hashtable]$params_array) { return $this.PostRequest($params_array) } } function New-RadioCodeCalculator { <# .SYNOPSIS Creates a Radio Code Calculator Web API client. .PARAMETER ApiKey Activation key from PELock. .EXAMPLE $client = New-RadioCodeCalculator -ApiKey 'ABCD-ABCD-ABCD-ABCD' $apiError, $result = $client.Calc([RadioModels]::Get([RadioModels]::FORD_M_SERIES), '123456') #> [CmdletBinding()] [OutputType([RadioCodeCalculator])] param( [string]$ApiKey ) if ([string]::IsNullOrWhiteSpace($ApiKey)) { return [RadioCodeCalculator]::new($null) } return [RadioCodeCalculator]::new($ApiKey) } Export-ModuleMember -Function @('New-RadioCodeCalculator') |