PSTello.psm1

using namespace System.Text
using namespace System.Net.Sockets
using namespace System.Net.IPAddress
using namespace System.Net.IPEndPoint

function Send-PSTelloCommand {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $socket,
        [Parameter(Mandatory)]
        [string]
        $Command
    )
    $EncodedText = [System.Text.Encoding]::ASCII.GetBytes($Command)
    $socket.send($EncodedText)
}

function Connect-PSTello {

    $socket = [System.Net.Sockets.Socket]::new([System.Net.Sockets.SocketType]::dgram, [System.Net.Sockets.ProtocolType]::Udp)
    $socket.Connect([System.Net.IPAddress]'192.168.10.1', 8889)
    $null = Enable-PSTelloDevMode -Socket $socket

    return $socket
}

function Enable-PSTelloDevMode {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket
    )
    Send-PSTelloCommand -Socket $Socket -Command 'command'
}

function Get-PSTelloState {

    $endpoint = [System.Net.IPEndPoint]::new([System.Net.IPAddress]'0.0.0.0', 8890)

    $client = [System.Net.Sockets.UdpClient]::new('8890')

    $client.Receive([ref]$endpoint)
}

function Get-PSTelloTelemetry {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket,
        [Parameter()]
        [ValidateSet('speed?','battery?','time?','height?','temp?','attitude?','baro?','acceleration?','tof?','wifi?')]
        [string]
        $Value = 'battery?'
    )
    Send-PSTelloCommand -Socket $Socket -Command $Value
}

function Move-PSTelloFlip {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket,
        [Parameter(Mandatory)]
        [ValidateSet(
            'left',
            'right',
            'forward',
            'back'
        )]
        [string]
        $Direction
    )
    Send-PSTelloCommand -Socket $Socket -Command ('flip {0}' -f $Direction.Substring(0,1))
}

function Move-PSTelloHorizontally {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket,
        [Parameter(Mandatory)]
        [ValidateSet(
            'left',
            'right',
            'forward',
            'back'
        )]
        [string]
        $Direction,
        [Parameter()]
        [ValidateRange(20,500)]
        [Int]
        $Distance = 20
    )
    Send-PSTelloCommand -Socket $Socket -Command ('{0} {1}' -f $Direction, $Distance)
}

function Move-PSTelloRotation {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket,
        [Parameter()]
        [ValidateSet(
            'ClockWise',
            'CounterClockWise'
        )]
        [string]
        $Direction = 'ClockWise',
        [Parameter()]
        [ValidateRange(1, 360)]
        [Int]
        $Degree = 180
    )

    switch ($Direction) {
        'ClockWise' { $rotationDirection = 'cw' }
        'CounterClockWise' { $rotationDirection = 'ccw' }
        Default { $rotationDirection = 'cw' }
    }
    Send-PSTelloCommand -Socket $Socket -Command ('{0} {1}' -f $rotationDirection, $Degree)
}

function Move-PSTelloVertically {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket,
        [Parameter(Mandatory)]
        [ValidateSet(
            'up',
            'down'
        )]
        [string]
        $Direction,
        [Parameter()]
        [ValidateRange(20,500)]
        [Int]
        $Distance = 20
    )
    Send-PSTelloCommand -Socket $Socket -Command ('{0} {1}' -f $Direction, $Distance)
}

function Send-PSTello {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket,
        [Parameter()]
        [ValidateSet('takeoff','land','streamon','streamoff','emergency')]
        [string]
        $Command = 'land'
    )
    Send-PSTelloCommand -Socket $Socket -Command $Command
}
function Set-PSTelloKeyBindings {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket
    )

    #region: Horizontal movement
    Set-PSReadLineKeyHandler -Key LeftArrow `
    -BriefDescription MoveLeft20cm `
        -LongDescription "Move the drone 20cm to the left" `
        -ScriptBlock {
        Move-PSTelloHorizontally -Socket $Socket -Direction 'left' -Distance 20
    }

    Set-PSReadLineKeyHandler -Key RightArrow `
    -BriefDescription MoveRight20cm `
        -LongDescription "Move the drone 20cm to the Right" `
        -ScriptBlock {
        Move-PSTelloHorizontally -Socket $Socket -Direction 'right' -Distance 20
    }

    Set-PSReadLineKeyHandler -Key UpArrow `
    -BriefDescription MoveForward20cm `
        -LongDescription "Move the drone 20cm to the front" `
        -ScriptBlock {
        Move-PSTelloHorizontally -Socket $Socket -Direction 'forward' -Distance 20
    }

    Set-PSReadLineKeyHandler -Key DownArrow `
    -BriefDescription MoveBackward20cm `
        -LongDescription "Move the drone 20cm to the back" `
        -ScriptBlock {
        Move-PSTelloHorizontally -Socket $Socket -Direction 'back' -Distance 20
    }
    #endregion

    #region: Vertical movement
    Set-PSReadLineKeyHandler -Key Ctrl+UpArrow `
    -BriefDescription MoveHigher20cm `
        -LongDescription "Move the drone 20cm to higher" `
        -ScriptBlock {
        Move-PSTelloVertically -Socket $Socket -Direction 'up' -Distance 20
    }

    Set-PSReadLineKeyHandler -Key Ctrl+DownArrow `
    -BriefDescription MoveLower20cm `
        -LongDescription "Move the drone 20cm to lower" `
        -ScriptBlock {
        Move-PSTelloVertically -Socket $Socket -Direction 'down' -Distance 20
    }
    #endregion

    #region: Rotation
    Set-PSReadLineKeyHandler -Key Ctrl+LeftArrow `
    -BriefDescription RotateCCW20cm `
        -LongDescription "Rotate the drone 90 degrees Counter-CockWise" `
        -ScriptBlock {
        Move-PSTelloRotation -Socket $Socket -Direction 'CounterClockWise' -Degree 90
    }

    Set-PSReadLineKeyHandler -Key Ctrl+RightArrow `
    -BriefDescription RotateCW20cm `
        -LongDescription "Rotate the drone 90 degrees CockWise" `
        -ScriptBlock {
        Move-PSTelloRotation -Socket $Socket -Direction 'ClockWise' -Degree 90
    }
    #endregion

    #region: Flip
    Set-PSReadLineKeyHandler -Key Shift+UpArrow `
    -BriefDescription FlipForward `
        -LongDescription "Flip the drone forwards" `
        -ScriptBlock {
        Move-PSTelloFlip -Socket $Socket -Direction 'forward'
    }

    Set-PSReadLineKeyHandler -Key Shift+DownArrow `
    -BriefDescription FlipBackward `
        -LongDescription "Flip the drone backwards" `
        -ScriptBlock {
        Move-PSTelloFlip -Socket $Socket -Direction 'back'
    }

    Set-PSReadLineKeyHandler -Key Shift+LeftArrow `
    -BriefDescription FlipLeft `
        -LongDescription "Flip the drone left" `
        -ScriptBlock {
        Move-PSTelloFlip -Socket $Socket -Direction 'left'
    }

    Set-PSReadLineKeyHandler -Key Shift+RightArrow `
    -BriefDescription FlipRight `
        -LongDescription "Flip the drone right" `
        -ScriptBlock {
        Move-PSTelloFlip -Socket $Socket -Direction 'right'
    }
    #endregion

    #region: TakeOff and landing
    Set-PSReadLineKeyHandler -Key Ctrl+Shift+UpArrow `
    -BriefDescription TakeOff `
        -LongDescription "Start the drone" `
        -ScriptBlock {
            Start-PSTello -Socket $Socket
    }

    Set-PSReadLineKeyHandler -Key Ctrl+Shift+DownArrow `
    -BriefDescription Land `
        -LongDescription "Land the drone" `
        -ScriptBlock {
        Stop-PSTello -Socket $Socket -Emergency $false
    }

    Set-PSReadLineKeyHandler -Key Escape `
    -BriefDescription Emergency `
        -LongDescription "Emergency land the drone" `
        -ScriptBlock {
        Stop-PSTello -Socket $Socket
    }
    #endregion
}

function Start-PSTello {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket
    )
    Send-PSTelloCommand -Socket $Socket -Command 'takeoff'
}

function Stop-PSTello {
    param (
        [Parameter(Mandatory)]
        [System.Net.Sockets.Socket]
        $Socket,
        [Parameter()]
        [bool]
        $Emergency = $true
    )
    if ($Emergency) {
        $command = 'emergency'
    } else {
        $command = 'land'
    }
    Send-PSTelloCommand -Socket $Socket -Command $command
}
Export-ModuleMember -Function Connect-PSTello,
Enable-PSTelloDevMode,
Get-PSTelloState,
Get-PSTelloTelemetry,
Move-PSTelloFlip,
Move-PSTelloHorizontally,
Move-PSTelloRotation,
Move-PSTelloVertically,
Send-PSTello,
Set-PSTelloKeyBindings,
Start-PSTello,
Stop-PSTello