Package: obd-socketio
obd-socketioQuickly create apython-socketioserver with events corresponding to thepython-OBDAPI. Ideal for controlling an Async connection to a vehicle from a JavaScript socketio client. The module provides default events which correspond to python-OBD methods as well as JSON encoding for its types.An example use case would be to use the server on a wireless hotspot enabled Raspberry Pi and be able to interact with the connection from another device in a browser. You can use the default events which will behave as python-OBD does or build your own custom websocket/OBD2 API.Installationpip install obd-socketioBasic Usageimport obdio

io = obdio.OBDio()

# Initiate connection to the vehicle
io.connect_obd()

# Create the server socket
io.create_server()  

# Start the server
io.run_server()APIConnecting to the VehicleConfiguring the ServerCreating a ServerRunning the ServerServing Static FilesThe Watch CallbackEventsCreating EventsDefault Server EventsJSON EncodingsRelated ProjectsConnecting to the VehicleUseconnect_obd()to initiate the serial connection with the vehicle.connect_obd()can be called at anytime during the server's life and can also be used in the body of an event handler.import obdio

io = obdio.OBDio()    

io.connect_obd('/dev/ttyUSB0')    # connect to specific serial portSeeOBD connectionsfor further usage.Configuring the ServerOBDio currently uses aUvicornASGI (Asynchronous Server Gateway Interface)socketio server for handling events asyncronously. Event handlers must be declaredasyncand emtting events must be called with theawaitkeyword.Creating a ServerTo create and access the socketio server usecreate_server(**kwargs)which passes its arguments to python-socketio'sServer classgiving you control over its behaviour as well as engineIO configuration.import obdio

io = obdio.OBDio()

sio = io.create_server(cors_allowed_origins='*', json=obdio)    # use obdio as the json module for obd supportThe json parameter can be substituted for your own or the built-in (import json) module though it cannot serialize some of the obd types.Running the ServerAfter creating your server socket you can define as many events or background tasks as needed on top of what is already defined by the OBDio class then callrun_server(**config)to listen on your desired host:port.Notable Parameters:host(str) - Use '0.0.0.0' to be accessible on your LAN.port(int) - the port the server will listen onMore config args can be found on the Uvicornsettings page. Must be called aftercreate_server.io.create_server()
io.run_server(host='0.0.0.0', port=48484)Serving Static FilesUseserve_static(files)to confugure the socketio server to serve a files by route, or a folder of static files. See python-socketioserver static filesfor more info.files(dict) - a dictionary of static files to serve by routeio = create_server()    # create the server first

io.server_static({
    '/': 'index.html'   # send some index for the route '/'
})

io.run_server()     # run the server after defining the static filesThe Watch CallbackWhen you create an OBDio instance, every OBD command added to the watch loop will be assigned a placeholder callback function calledwatch_callback. It is left for you to define so that data can be handled in your program uniquely. For example, for each response the value could be cached into a dictionary then that entire object streamed over the socket at a lesser rate thanwatch_callbackmay be fired.Your custom watch callback will be passed an OBDResponse. If you useobdioas your socket JSON serializer, the responses will be encoded correctly when you emit back to the client.response(obd.OBDResponse) - the obd value that triggered the callbackdata = {}
def cache_values(response):
    data[response.command.name] = response 

io.watch_callback = cache_valuesEventsWhen you create and run an OBDio server events are defined for the public python-OBD API which itself is powerful. However based on your use case you may want to add more functionality to the server beyond what is defined. Also, you may wish to override the default implementation of the predefined API to suit your needs.Creating EventsOne way to create custom or override events is to usecreate_event(name, handler).name(string) - the name of a custom or overriden eventasync handler(sid, data)(function) - the custom event handler - must be asyncYou are able to create custom events, or override the defaults with custom behaivour.sio = io.create_server()   # call create server first to access the socket

async def custom_handler(sid, data):    # handlers must be async
    await sio.emit('custom', data)      # any emits must be awaited

io.create_event('custom_event', custom_handler)

io.run_server(port=3000)     # lastly call run_serverOr, you can use the@sio.eventdecorator allowing you to create events as you would with an ASGI server.import obd

""" Override the default watch event """
@sio.event
async def watch(sid, cmd):
    io.connection.watch(obd.commands[cmd])    # access python-OBD connection and methods
    await sio.emit('event')     # emits must be awaited

io.run_server(port=48484)Default Server EventsOn creation of an OBDio server most of the python-OBD API is exposed through events of the same name as its functions. The server will handle the event then respond with data (if any) using the same event name.Event NameArgument TypeResponse Type'status'Nonestring'is_connected'Noneboolean'protocol_name'Nonestring'protocol_id'Nonestring'port_name'Nonestring'supports'string1boolean'supported_commands'Noneobject'query'2string1N/A'start'NoneN/A'watch'string[]1N/A'unwatch'string[]1N/A'unwatch_all'NoneN/A'has_name'string1boolean'close'NonenullArg is a list of OBD commands by name i.e. 'RPM', 'SPEED'. See the OBDCommand Tables.To query a command, it must bewatched firstSee the python-OBDdocumentationfor expected behaviour of the default events.JSON EncodingsCurrently the JSON encodings for the python-OBD types provide minimal data excluding unnessacary and non-human readable values to keep payload size smaller when emitting back to a client.OBDCommandname(string) - human readable representation of a PID, all capsdesc(string) - human readable description of the commandExample:{
    "name": "SPEED",
    "desc": "Vehicle Speed"
}OBDResponsevalue(any) - the response value: numeric, arraycommand(OBDCommand) - the command the response is fortime(number) - Unix timestamp (seconds) of the responseunit(string) - the unit of the response value. Non-numeric responses (dtcs, o2 sensors, etc) units will be represented as their type such as"<class 'tuple'>"while numeric ones will be a literal representing their unit from theobd unit registry(pint value).Example:{
    "value": 1500,
    "command": {
        "name": "RPM",
        "desc": "Engine RPM"
    },
    "time": 1639266637000,  // epoch time in ms
    "unit": "revolutions_per_minute"
}OBDResponse - Status Example:{
    "value": {
        "MIL": false,
        "DTC_COUNT": 0,
        "ignition_type": "spark"
    },
    "command": {
        "name": "STATUS",
        "desc": "Status since DTCs cleared"
    },
    "time": 1639341509038,
    "unit": "<class 'obd.OBDResponse.Status'>"
}Note the value is another object and unit describes the type of the response since it is not measurable.Relatedpython-OBDsocket.iopython-socketioELM327-emulatorUvicorn