{"id":34063229,"url":"https://github.com/vitalij555/bit-parser","last_synced_at":"2026-05-26T01:04:10.740Z","repository":{"id":62116864,"uuid":"458897876","full_name":"vitalij555/bit-parser","owner":"vitalij555","description":"Allows to define your own parser to parse bit fields or simple protocols.","archived":false,"fork":false,"pushed_at":"2025-12-24T13:52:44.000Z","size":64,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-05-05T23:07:09.601Z","etag":null,"topics":["bitmap","bits","bytes","fields","iot","library","parser","protocol","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vitalij555.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-13T18:37:56.000Z","updated_at":"2025-12-25T02:21:47.000Z","dependencies_parsed_at":"2023-01-22T02:50:46.302Z","dependency_job_id":null,"html_url":"https://github.com/vitalij555/bit-parser","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vitalij555/bit-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalij555%2Fbit-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalij555%2Fbit-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalij555%2Fbit-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalij555%2Fbit-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vitalij555","download_url":"https://codeload.github.com/vitalij555/bit-parser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalij555%2Fbit-parser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33499282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T14:31:05.219Z","status":"ssl_error","status_checked_at":"2026-05-25T14:31:02.878Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitmap","bits","bytes","fields","iot","library","parser","protocol","python"],"created_at":"2025-12-14T05:15:19.222Z","updated_at":"2026-05-26T01:04:10.733Z","avatar_url":"https://github.com/vitalij555.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Upload Python Package](https://github.com/vitalij555/bit-parser/workflows/Upload%20Python%20Package/badge.svg)\n[![PyPi version](https://img.shields.io/pypi/v/bit-parser.svg?style=flat-square) ](https://pypi.python.org/pypi/bit-parser) [![Supported Python versions](https://img.shields.io/pypi/pyversions/bit-parser.svg?style=flat-square) ](https://pypi.org/project/bit-parser) [![License](https://img.shields.io/pypi/l/bit-parser.svg?style=flat-square) ](https://choosealicense.com/licenses) [![Downloads](https://pepy.tech/badge/bit-parser)](https://pepy.tech/project/bit-parser) [![codecov](https://codecov.io/gh/vitalij555/bit-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/vitalij555/bit-parser)\n\n# bit-parser\n\nbit-parser is a small helper for decoding compact bitfields into human-readable labels.\n\nUse it when you receive short, fixed-size bitmaps where each bit has a meaning, or when a group of bits represents a value like a status code or counter. It is not intended for streaming protocols or large binary payloads, but it works well for logs, diagnostics, and UI tooling.\n\nInputs can be raw bytes or hex strings like `\"A3\"` (no spaces).\n\n## Contents\n\n- [Motivation](#motivation)\n- [Usage](#usage)\n  - [Simple example](#simple-example)\n  - [Advanced example](#advanced-example)\n  - [Full list output (for UI)](#full-list-output-for-ui)\n  - [Encode (reverse)](#encode-reverse)\n  - [Descriptor schema (for UI builders)](#descriptor-schema-for-ui-builders)\n- [Installation](#installation)\n- [API Overview](#api-overview)\n- [Tests](#tests)\n- [License](#license)\n- [Contributing](#contribute)\n\n\n## Motivation\n\nEmbedded and IoT devices often log compact status bytes instead of long, descriptive messages. Those hex values are fast for devices to output, but slow for humans to interpret.\n\nbit-parser lets you describe what each bit (or bit range) means and turn those bytes into readable labels or values.\n\n## Usage\n\n### Simple example\nImagine a controller returns one status byte where each bit represents the state of an I/O pin. A `1` means high voltage and `0` means low:\n\n```text\n# Byte 0:\n    Bit 7: I/O pin Nr7 high level\n    Bit 6: I/O pin Nr6 high level\n    Bit 5: I/O pin Nr5 high level\n    Bit 4: I/O pin Nr4 high level\n    Bit 3: I/O pin Nr3 high level\n    Bit 2: I/O pin Nr2 high level\n    Bit 1: I/O pin Nr1 high level\n    Bit 0: I/O pin Nr0 high level\n```\nYou can describe the bits and decode a response like this:\n\n```python\nfrom BitParser import parse_bits\nfrom pprint import pprint\n\nbits_meaning = [ \"I/O pin Nr7 high level\",\n                 \"I/O pin Nr6 high level\",\n                 \"I/O pin Nr5 high level\",\n                 \"I/O pin Nr4 high level\",\n                 \"I/O pin Nr3 high level\",\n                 \"I/O pin Nr2 high level\",\n                 \"I/O pin Nr1 high level\",\n                 \"I/O pin Nr0 high level\",]\n\n# Parse the input bytes or hex string\npprint(parse_bits(\"A3\", bits_meaning))\n```\nConsole output:\n```console\n['I/O pin Nr7 high level',\n 'I/O pin Nr5 high level',\n 'I/O pin Nr1 high level',\n 'I/O pin Nr0 high level']\n```\n\n### Advanced example\n\nOften, protocols pack multiple bits together to represent a value. For example, bits 2-4 in a byte can encode a status code. With 3 bits you can represent 8 statuses (000, 001, 010, ... 111), while other bits still represent a single flag.\n\nLet's consider a more advanced case.\n\nImagine we have a thermostat controller and the main module. Main manages temperature controller by sending it commands and obtaining back responses. For example, one of the responses could be represented by two bytes like following:\n\n```\n# Byte 0:\n \"sensor ID\",            | bit 7:    10000000\n \"sensor ID\",            | bit 6:    01000000\n \"sensor ID\",            | bit 5:    00100000\n \"temperature status\",   | bit 4:    00010000\n \"temperature status\",   | bit 3:    00001000\n \"LED is ON\",            | bit 2:    00000100\n \"heating mode\",         | bits 0-1: 000000xx\n# Byte 1:                |\n \"heating mode\",         | bits 6-7: xx000000\n \"heating module 1 on\",  | bit 5:    00100000\n \"heating module 2 on\",  | bit 4:    00010000\n \"heating module 3 on\",  | bit 3:    00001000\n \"heating module 4 on\",  | bit 2:    00000100\n \"RFU\",                  | bit 1:    00000010\n \"RFU\",                  | bit 0:    00000001\n\nWhere\nsensor ID - 3 bits representing device ID (meaning we can handle only 8 devices max in the system)\nTemperature status (2 bits) can have following values:\n    \"00\" - temperature OK\n    \"01\" - temperature too low\n    \"10\" - temperature too high\n    \"11\" - broken sensor\n\nHeating mode (4 bits):\n     0 - 0000 - mode off\n     1 - 0001 - mode 1\n     2 - 0010 - mode 2\n     3 - 0011 - mode 3\n     4 - 0100 - mode 4\n     5 - 0101 - mode 5\n     6 - 0110 - mode 6\n     7 - 0111 - mode 7\n     8 - 1000 - mode 8\n     9 - 1001 - RFU\n    10 - 1010 - RFU\n    11 - 1011 - RFU\n    12 - 1100 - RFU\n    13 - 1101 - RFU\n    14 - 1110 - RFU\n    15 - 1111 - RFU        \n```  \n\nFrom the description above, there are four cases to handle:\n1. Single-bit flags (e.g., \"heating module N on\" or \"RFU\")\n2. A multi-bit value where we care about the numeric value (sensor ID)\n3. A multi-bit value that spans bytes (heating mode)\n4. A single-bit status that should always be reported (LED ON/OFF)\n\nNow let's define a parser for these two bytes:\n\n```python\nfrom BitParser import parse_bits, MultiBitValueParser, SameValueRange\nfrom pprint import pprint\n\n# describing sensor_id \nsensor_id = MultiBitValueParser(SameValueRange(0b000, 0b111, 3, \"sensor ID\", return_value_instead_of_name=True))\n\n\n# describing Heating Mode\nheating_mode = MultiBitValueParser({ \"0000\": \"heating mode off\",\n                                     \"0001\": \"heating mode 1\",\n                                     \"0010\": \"heating mode 2\",\n                                     \"0011\": \"heating mode 3\",\n                                     \"0100\": \"heating mode 4\",  # important to describe full range here and not leave \n                                     \"0101\": \"heating mode 5\",\n                                     \"0110\": \"heating mode 6\",\n                                     \"0111\": \"heating mode 7\",\n                                     \"1000\": \"heating mode 8\"},  # here dictionary ends. We can have unlimited number of dictionaries or SameValueRange objects separated by comas inside MultiBitValueParser constructor.\n                                     SameValueRange(0b1001, 0b1111, 4, \"RFU\"))  # in this way we can define the whole range having same values\n\n# describing Status\nstatus = MultiBitValueParser({  \"00\": \"temperature OK\",\n                                \"01\": \"temperature too low\",\n                                \"10\": \"temperature too high\",\n                                \"11\": \"broken sensor\"})\n\n# LED ON/OFF\nled_status = MultiBitValueParser({ \"0\": \"LED is OFF\",\n                                   \"1\": \"LED is ON\"})\n\n# bringing all together\nadvanced_protocol = [\n                        # Byte 0:\n                         sensor_id,             # bit 7: 10000000   \n                         sensor_id,             # bit 6: 01000000\n                         sensor_id,             # bit 5: 00100000\n                         status,                # bit 4: 00010000\n                         status,                # bit 3: 00001000\n                         led_status,            # bit 2: 00000100\n                         heating_mode,          # bit 1: 00000010\n                         heating_mode,          # bit 0: 00000001\n                        # Byte 1:\n                         heating_mode,          # bit 7: 10000000\n                         heating_mode,          # bit 6: 01000000\n                         \"heating module 1 on\", # bit 5: 00100000\n                         \"heating module 2 on\", # bit 4: 00010000\n                         \"heating module 3 on\", # bit 3: 00001000\n                         \"heating module 4 on\", # bit 2: 00000100\n                         \"RFU\",                 # bit 1: 00000010\n                         \"RFU\",                 # bit 0: 00000001\n                      ]\n\ndef advanced_protocol_parser(bytes_to_parse):\n    return parse_bits(bytes_to_parse, advanced_protocol)\n\nadvanced_parser = advanced_protocol_parser\n\npprint(advanced_parser(\"40F0\"))\n```\nOutput:\n```console\n['sensor ID: 2',\n 'temperature OK',\n 'LED is OFF',\n 'heating mode 3',\n 'heating module 1 on',\n 'heating module 2 on']\n```\nTo see LED ON, set bit 2:\n```python\npprint(advanced_parser(\"4CF0\"))\n```\nOutput:\n```console\n['sensor ID: 2',\n 'temperature too low',\n 'LED is ON',\n 'heating mode 3',\n 'heating module 1 on',\n 'heating module 2 on']\n```\n\n### Full list output (for UI)\n\nIf you want to show every bit (enabled or not), use `parse_bits_full`. It returns:\n- One entry per bit with `enabled: True/False`\n- For multi-bit fields, an extra summary entry with the aggregated value\n\n```python\nfrom BitParser import parse_bits_full\n\ndescriptors = [\n    \"I/O pin Nr7 high level\",\n    \"I/O pin Nr6 high level\",\n    \"I/O pin Nr5 high level\",\n    \"I/O pin Nr4 high level\",\n    \"I/O pin Nr3 high level\",\n    \"I/O pin Nr2 high level\",\n    \"I/O pin Nr1 high level\",\n    \"I/O pin Nr0 high level\",\n]\n\nrows = parse_bits_full(\"80\", descriptors)\nprint(rows[0])\nprint(rows[-1])\n```\nExample output:\n```console\n{'kind': 'bit', 'label': 'I/O pin Nr7 high level', 'enabled': True, 'raw_bit': 1, ...}\n{'kind': 'bit', 'label': 'I/O pin Nr0 high level', 'enabled': False, 'raw_bit': 0, ...}\n```\n\n### Encode (reverse)\n\nUse `encode_bits` to generate a hex string from enabled labels and numeric multi-bit values.\nFor multi-bit fields you must provide either:\n- a label (e.g., `\"LED is ON\"`) in `enabled_labels`, or\n- a numeric value in `values` (e.g., `{\"heating mode\": 3}`).\nField names for `values` are inferred from the multi-bit labels (for example: `\"sensor ID\"` or `\"heating mode\"`).\nThis is handy when building web-based hex configuration generators or diagnostic calculators.\nIf a label appears multiple times (like `\"RFU\"`), use `label:byte:bit` to disambiguate (bytes are 0-based, bit 0 is LSB).\nYou can use the same `name:byte:bit` form in `values` when multiple multi-bit fields share the same name.\n\n```python\nfrom BitParser import encode_bits, MultiBitValueParser, SameValueRange\n\nheating_mode = MultiBitValueParser(\n    {\"0000\": \"heating mode off\",\n     \"0001\": \"heating mode 1\",\n     \"0010\": \"heating mode 2\",\n     \"0011\": \"heating mode 3\"}\n)\n\ndescriptors = [\n    \"I/O pin Nr7 high level\",\n    \"I/O pin Nr6 high level\",\n    \"I/O pin Nr5 high level\",\n    \"I/O pin Nr4 high level\",\n    \"I/O pin Nr3 high level\",\n    \"I/O pin Nr2 high level\",\n    \"I/O pin Nr1 high level\",\n    \"I/O pin Nr0 high level\",\n]\n\nhex_out = encode_bits(\n    [\"I/O pin Nr7 high level\", \"I/O pin Nr1 high level\", \"I/O pin Nr0 high level\"],\n    descriptors\n)\nprint(hex_out)  # \"83\"\n```\n\n### Descriptor schema (for UI builders)\n\nIf you want to build a UI without hardcoding the descriptor list, use `describe_bits`.\nIt returns a JSON-friendly schema with per-bit entries and multi-bit value options.\n\n```python\nfrom BitParser import describe_bits\n\nschema = describe_bits(descriptors)\nprint(schema[\"multi_bit\"][0][\"values\"][0])\n```\n\n## Installation\n\n```\npip install -U bit-parser\n```\n\n\n## API Overview\n\n### Functions\n\n- `parse_bits(bytes_or_hex, descriptors) -\u003e list[str]`  \n  Returns only enabled descriptors (bits with value 1) and aggregated multi-bit values.\n- `parse_bits_full(bytes_or_hex, descriptors) -\u003e list[dict]`  \n  Returns one entry for every bit, with `enabled` markers so UIs can grey out disabled bits. For multi-bit fields, also returns a summary entry with the aggregated value.\n- `encode_bits(enabled_labels, descriptors, values=None) -\u003e str`  \n  Returns an uppercase hex string from enabled labels and multi-bit numeric values. Use `label:byte:bit` to disambiguate.\n- `describe_bits(descriptors) -\u003e dict`  \n  Returns a JSON-friendly schema for UI builders (bits plus multi-bit value options).\n\n### Descriptor helpers\n\n- `MultiBitValueParser`  \n  Collects consecutive bits and maps the resulting bit string to a label.\n- `SameValueRange`  \n  Generates a mapping for a continuous range of values (useful for RFU or counters).\n\n### parse_bits_full output shape\n\nEach result entry is a `dict`.\n\nBit entry:\n- `kind`: `\"bit\"`\n- `label`: descriptor string (or `None` for multi-bit contributors)\n- `enabled`: `True`/`False`\n- `raw_bit`: `0`/`1`\n- `byte_index`, `bit_index`, `descriptor_index`\n- For multi-bit contributors: `group_id`, `group_bit_index`, `group_label`, `summary_index`\n\nSummary entry:\n- `kind`: `\"multi_bit\"`\n- `label`: evaluated value (e.g., `\"heating mode 3\"`)\n- `raw_bits`: assembled bit string\n- `value_int`: integer value of `raw_bits`\n- `group_id`, `descriptor_indices`\n\n## Tests\n\n[PyTest][pytest] is used for tests. Python 2 is not supported.\n\n**Install PyTest**\n\n```sh\n$ pip install pytest\n```\n\n**Run tests**\n\n```sh\n$ pytest test/*\n```\n\n[pytest]: http://pytest.org/\n\n**Check test coverage**\n\nIn order to generate test coverage report install pytest-cov:\n\n```sh\n$ pip install pytest-cov\n```\n\nThen inside test subdirectory call: \n\n```sh\npytest --cov=../BitParser --cov-report=html\n```\n\n## License\n\nLicense\nCopyright (C) 2022 Vitalij Gotovskij\n\nbit-parser binaries and source code can be used according to the MIT License\n\n\n## Contribute\nTBD\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitalij555%2Fbit-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitalij555%2Fbit-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitalij555%2Fbit-parser/lists"}