Skip to content

Commit e8f0c54

Browse files
committed
Built site for gh-pages
1 parent d26e57d commit e8f0c54

5 files changed

Lines changed: 3 additions & 57 deletions

File tree

.nojekyll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9e1808a3
1+
7f70e556

projects.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ <h5 class="no-anchor card-title listing-title">
432432
</div>
433433
</div></a>
434434
</div>
435-
<div class="g-col-1" data-index="2" data-categories="cHl0aG9u" data-listing-file-modified-sort="1767020880730" data-listing-reading-time-sort="8" data-listing-word-count-sort="1564">
435+
<div class="g-col-1" data-index="2" data-categories="cHl0aG9u" data-listing-file-modified-sort="1767024590543" data-listing-reading-time-sort="8" data-listing-word-count-sort="1405">
436436
<a href="./projects/pymotube/index.html" class="quarto-grid-link">
437437
<div class="quarto-grid-item card h-100 card-left">
438438
<p class="card-img-top">

projects/pymotube/index.html

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -511,55 +511,8 @@ <h2 class="anchored" data-anchor-id="a-logging-example">A Logging Example</h2>
511511
<span data-code-cell="annotated-cell-3" data-code-lines="12,13,14,15,16" data-code-annotation="4">Wait for data to appear on the queue, and then pass the data to <code>log_packet</code></span>
512512
</dd>
513513
</dl>
514-
</section>
515-
<section id="packets" class="level2">
516-
<h2 class="anchored" data-anchor-id="packets">Packets</h2>
517-
<section id="status-packet" class="level3">
518-
<h3 class="anchored" data-anchor-id="status-packet">Status Packet</h3>
519-
<p>The <code>StatusPacket</code> class takes a bytearray returned from the Status GATT characteristic and an optional timestamp and creates a data structure with fields corresponding to the different status flags:</p>
520-
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> atmotube <span class="im">import</span> StatusPacket</span>
521-
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
522-
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>example_status <span class="op">=</span> <span class="bu">bytearray</span>(<span class="st">b'Ad'</span>)</span>
523-
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>packet <span class="op">=</span> StatusPacket(example_status)</span>
524-
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
525-
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(packet)</span>
526-
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co"># StatusPacket(pm_sensor_status=True, error_flag=False, bonding_flag=False, charging=False, charging_timer=False, pre_heating=True, battery_level=100%)</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
527-
</section>
528-
<section id="sps30-packet" class="level3">
529-
<h3 class="anchored" data-anchor-id="sps30-packet">SPS30 Packet</h3>
530-
<p>The <code>SPS30Packet</code> takes a bytearray returned from the SPS30 GATT characteristic with an optional timestamp and creates a data structure with fields corresponding to the PM measurements in ug/m^3</p>
531-
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> atmotube <span class="im">import</span> SPS30Packet</span>
532-
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
533-
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>example_sps30 <span class="op">=</span> <span class="bu">bytearray</span>(<span class="st">b'd</span><span class="ch">\x00\x00\xb9\x00\x00</span><span class="st">J</span><span class="ch">\x01\x00</span><span class="st">o</span><span class="ch">\x00\x00</span><span class="st">'</span>)</span>
534-
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>packet <span class="op">=</span> SPS30Packet(example_sps30)</span>
535-
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
536-
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(packet)</span>
537-
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="co"># SPS30Packet(pm1=1.0µg/m³, pm2_5=1.85µg/m³, pm10=3.3µg/m³, pm4=1.11µg/m³)</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
538-
</section>
539-
<section id="bme280-packet" class="level3">
540-
<h3 class="anchored" data-anchor-id="bme280-packet">BME280 Packet</h3>
541-
<p>The <code>BME280Packet</code> takes a bytearray returned from the BME280 GATT characteristic with an optional timestamp and creates a datastructure with fields corresponding the temperature, pressure, and humidity.</p>
542-
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> atmotube <span class="im">import</span> BME280Packet</span>
543-
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
544-
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>example_bme280 <span class="op">=</span> <span class="bu">bytearray</span>(<span class="st">b'</span><span class="ch">\x0e\x17\x8a</span><span class="st">o</span><span class="ch">\x01\x00\x1a\t</span><span class="st">'</span>)</span>
545-
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>packet <span class="op">=</span> BME280Packet(example_bme280)</span>
546-
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
547-
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(packet)</span>
548-
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co"># BME280Packet(humidity=14%, temperature=23.3°C, pressure=940.9mbar)</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
549-
</section>
550-
<section id="sgpc3-packet" class="level3">
551-
<h3 class="anchored" data-anchor-id="sgpc3-packet">SGPC3 Packet</h3>
552-
<p>The <code>SGPC3Packet</code> takes a bytearray returned from the SGPC3 GATT characteristic with an optional timestamp and creates a datastructure with a single field for the VOC.</p>
553-
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> atmotube <span class="im">import</span> SGPC3Packet</span>
554-
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
555-
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>example_sgpc3 <span class="op">=</span> <span class="bu">bytearray</span>(<span class="st">b'</span><span class="ch">\x02\x00\x00\x00</span><span class="st">'</span>)</span>
556-
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>packet <span class="op">=</span> SGPC3Packet(example_sgpc3)</span>
557-
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
558-
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(packet)</span>
559-
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="co"># SGPC3Packet(tvoc=0.002ppb)</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
560514

561515

562-
</section>
563516
</section>
564517

565518
</main> <!-- /main -->

search.json

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -804,13 +804,6 @@
804804
"section": "A Logging Example",
805805
"text": "A Logging Example\nAs an example of how to use this, consider the case where you want to connect to your AtmoTube from a PC, log data from it over a pre-defined period, then exit. This will be done asynchronously and the retrieved data put into an asynchronous queue for processing.\nThe first step is to create a function which connects to the AtmoTube, collects data, and then puts that data into a queue.\nfrom bleak import BleakClient, BleakScanner\nfrom atmotube import start_gatt_notifications, get_available_services\nimport asyncio\n\n\nasync def collect_data(mac, queue, collection_time):\n1 async def callback_queue(packet):\n await queue.put(packet)\n\n2 device = await BleakScanner.find_device_by_address(mac)\n if not device:\n raise Exception(\"Device not found\")\n\n3 async with BleakClient(device) as client:\n if not client.is_connected:\n raise Exception(\"Failed to connect to device\")\n4 packet_list = get_available_services(client)\n5 await start_gatt_notifications(client, callback_queue,\n packet_list=packet_list)\n6 await asyncio.sleep(collection_time)\n7 await queue.put(None)\n\n1\n\nDefine a callback function which takes a packet of data and does something with it. In this case it puts it in an asynchronous queue.\n\n2\n\nFind the device using Bleak, in this case by mac address\n\n3\n\nConnect to the device using Bleak\n\n4\n\nCall the get_available_services function with the connected device, this generates a list of GATT services that both the atmotube library knows about and the AtmoTube device supports.\n\n5\n\nStart the GATT notifications for the list of available services. If no list is provided, it will attempt to start GATT notifications for all services supported by the atmotube library.\n\n6\n\nWait while data is collected, for the pre-defined collection time (in seconds)\n\n7\n\nPut a None on the queue to indicate that the collection has ended.\n\n\nWhat ends up on the queue is a series of AtmoTubePacket objects representing the different types of data packets the AtmoTube returns. Each packet has an associated datetime object representing the time when the packet was received. As a somewhat silly example, this takes those packets and logs them to the logger – a more realistic thing to do might be to put the data in a database or append the data to a CSV.\nfrom atmotube import SPS30Packet, StatusPacket, BME280Packet, SGPC3Packet\n\nimport logging\n\n\ndef log_packet(packet):\n1 match packet:\n case StatusPacket():\n2 logging.info(f\"{str(packet.date_time)} - Status Packet - \"\n f\"Battery: {packet.battery_level}%, \"\n f\"PM Sensor: {packet.pm_sensor_status}, \"\n f\"Pre-heating: {packet.pre_heating}, \"\n f\"Error: {packet.error_flag}\")\n case SPS30Packet():\n logging.info(f\"{str(packet.date_time)} - SPS30 Packet - \"\n f\"PM1: {packet.pm1} µg/m³, \"\n f\"PM2.5: {packet.pm2_5} µg/m³, \"\n f\"PM4: {packet.pm4} µg/m³, \"\n f\"PM10: {packet.pm10} µg/m³\")\n case BME280Packet():\n logging.info(f\"{str(packet.date_time)} - BME280 Packet - \"\n f\"Humidity: {packet.humidity}%, \"\n f\"Temperature: {packet.temperature}°C, \"\n f\"Pressure: {packet.pressure} mbar\")\n case SGPC3Packet():\n logging.info(f\"{str(packet.date_time)} - SGPC3 Packet - \"\n f\"TVOC: {packet.tvoc} ppb\")\n case _:\n logging.info(\"Unknown packet type\")\n\n1\n\nUse structural pattern matching to identify which data has been returned.\n\n2\n\nSend some information about it to the logger\n\n\nFinally, an asynchronous event loop is created which runs the collector and then logs the data.\nATMOTUBE = \"00:00:00:00:00:00\" # the mac address of the ATMOTUBE\n\ndef main():\n mac = ATMOTUBE\n collection_time = 60 # seconds\n1 queue = asyncio.Queue()\n\n2 async def runner():\n3 collector = asyncio.create_task(\n collect_data(mac, queue, collection_time)\n )\n4 while True:\n item = await queue.get()\n if item is None:\n break\n log_packet(item)\n await collector\n\n asyncio.run(runner())\n\n\nif __name__ == \"__main__\":\n logging.basicConfig(level=logging.INFO)\n main()\n\n1\n\nInitialize an asynchronous queue, this will be used to pass the data between the two workers\n\n2\n\nCreate a runner function to handle the main sequence of events\n\n3\n\nStart the collector\n\n4\n\nWait for data to appear on the queue, and then pass the data to log_packet"
806806
},
807-
{
808-
"objectID": "projects/pymotube/index.html#packets",
809-
"href": "projects/pymotube/index.html#packets",
810-
"title": "PymoTube",
811-
"section": "Packets",
812-
"text": "Packets\n\nStatus Packet\nThe StatusPacket class takes a bytearray returned from the Status GATT characteristic and an optional timestamp and creates a data structure with fields corresponding to the different status flags:\nfrom atmotube import StatusPacket\n\nexample_status = bytearray(b'Ad')\npacket = StatusPacket(example_status)\n\nprint(packet)\n# StatusPacket(pm_sensor_status=True, error_flag=False, bonding_flag=False, charging=False, charging_timer=False, pre_heating=True, battery_level=100%)\n\n\nSPS30 Packet\nThe SPS30Packet takes a bytearray returned from the SPS30 GATT characteristic with an optional timestamp and creates a data structure with fields corresponding to the PM measurements in ug/m^3\nfrom atmotube import SPS30Packet\n\nexample_sps30 = bytearray(b'd\\x00\\x00\\xb9\\x00\\x00J\\x01\\x00o\\x00\\x00')\npacket = SPS30Packet(example_sps30)\n\nprint(packet)\n# SPS30Packet(pm1=1.0µg/m³, pm2_5=1.85µg/m³, pm10=3.3µg/m³, pm4=1.11µg/m³)\n\n\nBME280 Packet\nThe BME280Packet takes a bytearray returned from the BME280 GATT characteristic with an optional timestamp and creates a datastructure with fields corresponding the temperature, pressure, and humidity.\nfrom atmotube import BME280Packet\n\nexample_bme280 = bytearray(b'\\x0e\\x17\\x8ao\\x01\\x00\\x1a\\t')\npacket = BME280Packet(example_bme280)\n\nprint(packet)\n# BME280Packet(humidity=14%, temperature=23.3°C, pressure=940.9mbar)\n\n\nSGPC3 Packet\nThe SGPC3Packet takes a bytearray returned from the SGPC3 GATT characteristic with an optional timestamp and creates a datastructure with a single field for the VOC.\nfrom atmotube import SGPC3Packet\n\nexample_sgpc3 = bytearray(b'\\x02\\x00\\x00\\x00')\npacket = SGPC3Packet(example_sgpc3)\n\nprint(packet)\n# SGPC3Packet(tvoc=0.002ppb)"
813-
},
814807
{
815808
"objectID": "projects/gas_dispersion_jl/index.html",
816809
"href": "projects/gas_dispersion_jl/index.html",

sitemap.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
</url>
7979
<url>
8080
<loc>https://aefarrell.github.io/projects/pymotube/index.html</loc>
81-
<lastmod>2025-12-29T15:08:00.730Z</lastmod>
81+
<lastmod>2025-12-29T16:09:50.543Z</lastmod>
8282
</url>
8383
<url>
8484
<loc>https://aefarrell.github.io/projects/gas_dispersion_jl/index.html</loc>

0 commit comments

Comments
 (0)