Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Proposal: REST/WebSocket API #916

Closed
wants to merge 1 commit into from

Conversation

Kavakuo
Copy link
Contributor

@Kavakuo Kavakuo commented Sep 25, 2022

Hi,

I would like to propose a new "major" feature for MobiFlight, a REST/WebSocket API that can be used to trigger inputs and receive output values.

Motivation

I started to play around with a minimal Arduino setup and a single encoder. Instead of having multiple encoders, I want to use this single encoder to control multiple functions in the cockpit. For this I utilized the MobiFlight preconditions, variables and joystick inputs to switch between different encoder modes. So pressing a button on a Joystick sets a MobiFlight variable and multiple encoder input configs with preconditions control which FlightSim variable updates. So far, so good.

The main problem with this approach is that this is only one way. It is not possible to get any information out of MobiFlight in which mode the encoder currently operates. This is where the REST/WebSocket API comes into play.

In the bigger picture, I think this would enable a completely new kind of applications to interact with MobiFlight and profit from the existing features, like the super nice Huphop integration. Stream Deck Plugins that can visualize output variables, iPad/Web Apps to build dashboards and probably many more.

REST/WebSocket API

The idea is to have a new device category (REST/WebSocket API) that can be used as input and output device.

UI Overview

Input

REST/WebSocket API can be selected as an Input module. The value entered into the Device text field is basically the device identifier used with the API. For example, an HTTP POST request to /input/[device identifier] would trigger the input using the value from the body of the POST request.

Internally, this is handled as an AnalogInput. This might not be the cleanest solution, let me know what you think about it. It just was the easiest way to reuse the existing architecture.

image

Output

Similar to the inputs, I added a new display type for the outputs as well. In contrast to the inputs, each output must have a unique device/endpoint name. The current value of an output can be received by sending an HTTP GET Request to /output/[device name].

The downside of only using a REST API is that it would be necessary to poll for new output values. Because of that, I also decided to implement a WebSocket API, so that messages could be pushed to clients when an output value changes.

image

Settings

The settings UI is pretty straight forward. The interface where the REST and WebSocket Server is listening can be configured (either 127.0.0.1 or All Interfaces) as well as a TCP port.

image

Supported API Operations

  • Trigger input

    • REST API POST /input/[device name], value inside POST body. Response, see WebSocket response below.
    • WebSocket API
      • Request
      {
          "MessageType": "TRIGGER_INPUT",
          "Input": "[device name]",
          "Value": "[value]"
      }
      • Response
      {
          "Message": "ok",
          "MessageType": "SUCCESS"
      }
  • Get value of REST/WebSocket API output device

    • REST API GET /output/[device name], see WebSocket response below.
    • WebSocket API
      • Request
      {
          "MessageType": "GET_OUTPUT",
          "Output": "[device name]"
      }
      • Response
      {
          "Output": "[device name]",
          "Value": "[value]",
          "MessageType": "GET_OUTPUT"
      }
  • Get value of all REST/WebSocket API output devices

    • REST API GET /output/, see WebSocket response below.
    • WebSocket API
      • Request
      {
          "MessageType": "GET_ALL_OUTPUTS"
      }
      • Response
      {
          "OutputValues": {
              "[device name]": "[value]"
          },
          "MessageType": "GET_ALL_OUTPUTS"
      }
  • When an output value changed (WebSocket only)

    {
        "Output": "[device name]",
        "Value": "[value]",
        "MessageType": "OUTPUT_CHANGED"
    }
  • Error message (response only)

    {
        "ErrorMsg": "[reason]",
        "MessageType": "ERROR"
    }

Architecture Details

  • As REST/WebSocket server, I added EmbedIO as dependency (MIT License).

  • The RestApiManager class is the entry point that starts both servers and is part of the ExecutionManager class.

  • RestApiServer and WebSocketApiServer include the server methods that are called, when a new message is received. Those classes are pretty small.

  • The whole message processing is done in the MessageProcessor class.

    • The incoming message is deserialized into one of the message classes (namespace MobiFlight.RestWebSocketApi.Messages), depending on the MessageType field of the request.
    • Depending on the message type, the appropriate action is triggered, and a response sent to the client.
  • Supported messages are defined in the MessageTypeEnum. Adding new messages should be straight-forward.

What needs to be done (at least)

  • Unit Tests (didn't look into the test architecture yet)
  • UI Localization

Feedback

Please let me know what you think about this proposal. I am open to discuss any changes, maybe you have some additional/other ideas/wishes for the API. Even if you see no future in this feature, that's fine as well. Just let me know.

Best regards,
Philipp

@MobiFlight-Admin
Copy link
Collaborator

MobiFlight-Admin commented Sep 27, 2022

Thank you very much for your effort.
I will take a look, but I have implemented it already myself too. A little differently though. I didn't model it as device, but as a new Output Type. It works really great.

@DocMoebiuz
Copy link
Collaborator

btw, here is the Websocket POC branch: https://github.com/MobiFlight/MobiFlight-Connector/tree/poc-websocket

@DocMoebiuz
Copy link
Collaborator

@Kavakuo This will really be a great addition, your implementation is far more complete than my PoC because you are supporting inputs. I would defintely like to take your version as a starting point and then finish it up. Or would you like to further collaborate on this?

@DocMoebiuz
Copy link
Collaborator

Thanks for sharing... I will add this soon from scratch. But thanks anyways!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants