Package: siobrultech-protocols
What is siobrultech-protocols?This library is a collection of protcols that decode various packet formats fromBrultech Research.What is Sans-I/O?Sans-I/O is a philosophy for developing protocol processing libraries in which
the library does not do any I/O. Instead, a user of the library is responsible
for transferring blocks of bytes between the socket or pipe and the protocol
library, and for receiving application-level protocol items from and sending
them to the library. This obviously makes a sans-I/O library a little more
difficult to use, but comes with the advantage that the same library can be
used with any I/O and concurrency mechanism: the same library should be usable
in a single-request-at-a-time server, a process-per-request or
thread-per-request blocking server, a server using select/poll and
continuations, or a server using asyncio, Twisted, or any other asynchronous
framework.SeeSansIOfor more information.Installationpip install siobrultech-protocolsUsageReceiving data packetsimportfunctoolsfromsiobrultech_protocols.gem.protocolimportPacketProtocol,PacketReceivedMessage# Queue to get received packets from.queue=asyncio.Queue()# Pass this Protocol to whatever receives data from the device.protocol_factory=functools.partial(PacketProtocol,queue=queue)# Dequeue and look for packet received messages. (Typically do this in a loop.)message=awaitqueue.get()ifisinstance(message,PacketReceivedMessage):packet=message.packetqueue.task_done()Receiving data packets AND sending API commandsIf you want to send API commands as well, use aBidirectionalProtocolinstead of aPacketProtocol. Then given theprotocolinstance for a given connection, do the API call as follows:fromsiobrultech_protocols.gem.apiimportget_serial_numberserial=awaitget_serial_number(protocol)siobrultech_protocolsprovides direct support for a small set of API calls. Some of these calls work for both GEM and ECM monitors, others are GEM-only.Methods to Get Information from a DeviceMethodGEMECMDescriptionget_serial_number✅︎✅︎Obtains the serial number of the device.Methods to Setup a DeviceMethodGEMECMDescriptionset_date_and_time✅︎❌Sets the GEM's clock to the specifieddatetime.set_packet_format✅︎❌Sets the GEM's packet format to the specifiedPacketFormatType.set_packet_send_interval✅︎✅︎Sets the frequency (seconds) that the monitor should send packets.set_secondary_packet_format✅︎❌Sets the GEM's secondary packet format to the specifiedPacketFormatType.synchronize_time✅︎❌Synchronizes the GEM's clock to the time of the local device.Calling API endpoints that aren't supported by this librarysiobrultech_protocolshas built-in support for just a tiny subset of the full API exposed by GEM and ECM. If you want to call an API endpoint for which this library doesn't provide a helper, you can make your own. For example, the following outline could be filled in to support the "get all settings" endpoint; you could defineGET_ALL_SETTINGS:fromsiobrultech_protocols.gemimportapi# Define a Python data type for the response. It can be whatever you want; a simple Dict, a custom dataclass, etc.AllSettings=Dict[str,Any]def_parse_all_gem_settings(response:str)->AllSettings:# Here you would parse the GEM response into the python type you defined abovedef_parse_all_ecm_settings(response:bytes)->AllSettings:# Here you would parse the ECM response into the python type you defined aboveGET_ALL_SETTINGS=api.ApiCall[None,AllSettings](gem_formatter=lambda_:"^^^RQSALL",gem_parser=_parse_all_gem_settings,ecm_formatter=lambda_:[b"\xfc",b"SET",b"RCV"],ecm_parser=_parse_all_ecm_settings,)Given anApiCall(whether one of those insiobrultech_protocols.apior defined yourself as above), you can
make the request by working with the protocol directly as follows:# Start the API request; do this once for each API call. Each protocol instance can only support one# API call at a time.delay=protocol.begin_api_request()sleep(delay)# Wait for the specified delay, using whatever mechanism is appropriate for your environment# Send the API request. We use asyncio's implementation of Future, but you can use whatever you like.result=asyncio.get_event_loop().create_future()protocol.invoke_api(GET_ALL_SETTINGS,None,result)settings=awaitasyncio.wait_for(result,timeout=5)# End the API requestprotocol.end_api_request()Alternatively, we also provide a context wrapper that works withasyncioas well:fromsiobrultech_protocols.gemimportapiasyncwithapi.call_api(GET_ALL_SETTINGS,protocol)asf:settings=awaitf(None)Take a look at some usage examples fromlibraries that use this.Calling API endpoints when multiple devices share a connectionAll of the API helper methods take an optionalserial_numberparameter to target a specific device if there are multiple devices on the same connection. This has no effect for ECM devices.DevelopmentSetuppython3.11 -m venv .venv
source .venv/bin/activate

# Install Requirements
pip install -r requirements.txt

# Install Dev Requirements
pip install -r requirements-dev.txtTestingTests are run withpytest.LintingLint can be run withEarthlywith./earthly.sh +lint