Mercurial > code > home > repos > doorbell
changeset 2:f822e7fe7120
rewrite to use telemetrix (localized)
author | drewp@bigasterisk.com |
---|---|
date | Sun, 05 Feb 2023 14:06:19 -0800 |
parents | 125c794511a6 |
children | 67402d8b4e0d |
files | doorbell_to_mqtt.py pdm.lock pyproject.toml |
diffstat | 3 files changed, 216 insertions(+), 56 deletions(-) [+] |
line wrap: on
line diff
--- a/doorbell_to_mqtt.py Sun Feb 05 14:05:06 2023 -0800 +++ b/doorbell_to_mqtt.py Sun Feb 05 14:06:19 2023 -0800 @@ -1,60 +1,97 @@ -import time +import asyncio import logging import sys -import paho.mqtt.client as mqtt -from pyfirmata import Arduino, INPUT -PULLUP = 0x0b +import asyncio_mqtt +import uvicorn +from starlette.applications import Starlette +from starlette.requests import Request +from starlette.responses import HTMLResponse, RedirectResponse +from starlette.routing import Route +from typing import Optional +import telemetrix_local as telemetrix_aio -MIN_REPEAT_SEC = .5 +DIGITAL_PIN = 2 # arduino pin number +KILL_TIME = 5 # sleep time to keep forever loop open +MQTT_RECONNECT_SEC = 5 +MIN_REPEAT_SEC = 2 -logging.basicConfig(level=logging.INFO) +logging.basicConfig(level=logging.DEBUG) log = logging.getLogger() dev, = sys.argv[1:] -client = mqtt.Client() -client.will_set('doorbell/online', '0', qos=0, retain=False) -client.connect('bang') -log.info(f'connecting to {dev}') -# note that I set StandardFirmata to double baud rate -board = Arduino(dev, baudrate=115200) -log.info('done') +async def digital_in_pullup(my_board, pin): + last_report = 0 -pin = board.get_pin('d:2:i') -pin.mode = PULLUP # config msg to send to firmata -pin._mode = INPUT # fake value so pyfirmata still reads this pin + async def cb(data): + nonlocal last_report + _pinmode, _pin, value, t = data + pressed = not value + if pressed: + if last_report < t - MIN_REPEAT_SEC: + log.info(f'press {t}') + await press() + last_report = t -log.info('running mqtt client') -client.loop_start() -log.info('done') - -client.publish('doorbell/online', '1') - -last_value = None -last_edge = 0 + await my_board.set_pin_mode_digital_input_pullup(pin, cb) + await board.enable_digital_reporting(2) + while True: + await asyncio.sleep(KILL_TIME) -def on_press(t): - global last_edge - if t > last_edge + MIN_REPEAT_SEC: - log.info(f'press {t=}') - client.publish('doorbell/button', 'press') - log.info('published') - last_edge = t +board = telemetrix_aio.TelemetrixAIO(com_port=dev, autostart=False) + +cli: Optional[asyncio_mqtt.Client] =None + +async def press(): + await cli.publish('doorbell/button', 'pressed') + +async def status(request: Request) -> HTMLResponse: + return HTMLResponse("""<doctype HTML> + <html> + <head><title>doorbell</title></head> + <body> + <form method="POST" action="press"> + <button type="submit">test ring</button> + </form> + </body> + </html> + """) + +async def post_press(request: Request) -> RedirectResponse: + await press() + return RedirectResponse('.', status_code=303) + -try: - while True: - now = time.time() - board.iterate() - value = pin.read() - if value == 0 and last_value == 1: - on_press(now) - last_value = value - client.loop() -except Exception: - client.publish('doorbell/online', '0') - client.disconnect() - raise \ No newline at end of file +app = Starlette(routes=[ + Route('/', status), + Route('/press', post_press, methods=['POST']), +]) + + +async def main(): + global cli + config = uvicorn.Config(app, host='0.0.0.0', port=8000, log_level="info") + server = uvicorn.Server(config) + + online = 'doorbell/online' + will = asyncio_mqtt.Will(topic=online, payload='0') + async with asyncio_mqtt.Client( + "bang", + keepalive=10, # don't go below firmata startup time of 5sec + will=will) as client: + log.debug(f'with {client=}') + cli=client + await board.start_aio() + + t1 = asyncio.create_task(server.serve()) + t2 = asyncio.create_task(digital_in_pullup(board, 2)) + await client.publish(online, '1') + while True: + await asyncio.sleep(KILL_TIME) + + +asyncio.run(main(), debug=True) \ No newline at end of file
--- a/pdm.lock Sun Feb 05 14:05:06 2023 -0800 +++ b/pdm.lock Sun Feb 05 14:06:19 2023 -0800 @@ -1,34 +1,153 @@ +[[package]] +name = "anyio" +version = "3.6.2" +requires_python = ">=3.6.2" +summary = "High level compatibility layer for multiple asynchronous event loop implementations" +dependencies = [ + "idna>=2.8", + "sniffio>=1.1", +] + +[[package]] +name = "asyncio-mqtt" +version = "0.16.1" +requires_python = ">=3.7" +summary = "Idiomatic asyncio wrapper around paho-mqtt" +dependencies = [ + "paho-mqtt>=1.6.0", +] + +[[package]] +name = "click" +version = "8.1.3" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +dependencies = [ + "colorama; platform_system == \"Windows\"", +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." + +[[package]] +name = "h11" +version = "0.14.0" +requires_python = ">=3.7" +summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" + +[[package]] +name = "idna" +version = "3.4" +requires_python = ">=3.5" +summary = "Internationalized Domain Names in Applications (IDNA)" + [[package]] name = "paho-mqtt" version = "1.6.1" summary = "MQTT version 5.0/3.1.1 client class" [[package]] -name = "pyfirmata" -version = "1.1.0" -summary = "A Python interface for the Firmata procotol" +name = "pyserial" +version = "3.5" +summary = "Python Serial Port Extension" + +[[package]] +name = "pyserial-asyncio" +version = "0.6" +summary = "Python Serial Port Extension - Asynchronous I/O support" +dependencies = [ + "pyserial", +] + +[[package]] +name = "sniffio" +version = "1.3.0" +requires_python = ">=3.7" +summary = "Sniff out which async library your code is running under" + +[[package]] +name = "starlette" +version = "0.23.1" +requires_python = ">=3.7" +summary = "The little ASGI library that shines." +dependencies = [ + "anyio<5,>=3.4.0", +] + +[[package]] +name = "telemetrix-aio" +version = "1.11" +summary = "Remotely Control And Monitor Arduino-Core devices" dependencies = [ "pyserial", ] [[package]] -name = "pyserial" -version = "3.5" -summary = "Python Serial Port Extension" +name = "uvicorn" +version = "0.20.0" +requires_python = ">=3.7" +summary = "The lightning-fast ASGI server." +dependencies = [ + "click>=7.0", + "h11>=0.8", +] [metadata] lock_version = "4.0" -content_hash = "sha256:0a4f0629cf61e777ba036a8c5120073441e264d2acd07f19e4bae0816a7521ff" +content_hash = "sha256:6d9a6594c6d7a72bfc8de7484e6176d33e1306564ca0de1d697c83601cd12ca1" [metadata.files] +"anyio 3.6.2" = [ + {url = "https://files.pythonhosted.org/packages/77/2b/b4c0b7a3f3d61adb1a1e0b78f90a94e2b6162a043880704b7437ef297cad/anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {url = "https://files.pythonhosted.org/packages/8b/94/6928d4345f2bc1beecbff03325cad43d320717f51ab74ab5a571324f4f5a/anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, +] +"asyncio-mqtt 0.16.1" = [ + {url = "https://files.pythonhosted.org/packages/28/0f/008e00d479dc7c666e8cef37d8f738e330a525d6f01fcd351d7fce71681b/asyncio_mqtt-0.16.1-py3-none-any.whl", hash = "sha256:ef12c1d0250ffd65b70085d66c457128ed6aa98b3d821d2316213fb9d1957d5b"}, + {url = "https://files.pythonhosted.org/packages/b9/ae/bb75e07de548e883ef0b813f139613f13ac43e79874b1365af21572a7eaa/asyncio_mqtt-0.16.1.tar.gz", hash = "sha256:38416481bb7cc9613c6a1a8e58b8f9f9c9a522d93411003e8000e74ef2b7ed85"}, +] +"click 8.1.3" = [ + {url = "https://files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {url = "https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, +] +"colorama 0.4.6" = [ + {url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +"h11 0.14.0" = [ + {url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] +"idna 3.4" = [ + {url = "https://files.pythonhosted.org/packages/8b/e1/43beb3d38dba6cb420cefa297822eac205a277ab43e5ba5d5c46faf96438/idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {url = "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, +] "paho-mqtt 1.6.1" = [ {url = "https://files.pythonhosted.org/packages/f8/dd/4b75dcba025f8647bc9862ac17299e0d7d12d3beadbf026d8c8d74215c12/paho-mqtt-1.6.1.tar.gz", hash = "sha256:2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f"}, ] -"pyfirmata 1.1.0" = [ - {url = "https://files.pythonhosted.org/packages/14/f0/05e30c9cee38f9c0e8ef08a07ff28e4c391c7d95e2bcb1f334f58cc2bb28/pyFirmata-1.1.0-py2.py3-none-any.whl", hash = "sha256:03091ffd0b06a483d286d9738ead97a50c55cf1bf6b89a5bd0c420d0a7797420"}, - {url = "https://files.pythonhosted.org/packages/ff/e6/512cc154c0370e22429db3139e7c906c4f8ad2646f4ca816547e9ae2c26c/pyFirmata-1.1.0.tar.gz", hash = "sha256:cc180d1b30c85a2bbca62c15fef1b871db048cdcfa80959968356d97bd3ff08e"}, -] "pyserial 3.5" = [ {url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, {url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, ] +"pyserial-asyncio 0.6" = [ + {url = "https://files.pythonhosted.org/packages/27/24/c820cf15f87f7b164e83710c1852d4f900d9793961579e5ef64189bc0c10/pyserial_asyncio-0.6-py3-none-any.whl", hash = "sha256:de9337922619421b62b9b1a84048634b3ac520e1d690a674ed246a2af7ce1fc5"}, + {url = "https://files.pythonhosted.org/packages/4a/9a/8477699dcbc1882ea51dcff4d3c25aa3f2063ed8f7d7a849fd8f610506b6/pyserial-asyncio-0.6.tar.gz", hash = "sha256:b6032923e05e9d75ec17a5af9a98429c46d2839adfaf80604d52e0faacd7a32f"}, +] +"sniffio 1.3.0" = [ + {url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] +"starlette 0.23.1" = [ + {url = "https://files.pythonhosted.org/packages/0c/d2/a2898deb36d12e40e84e83b7728628a04013cb0cfc545932c4605185bf2d/starlette-0.23.1.tar.gz", hash = "sha256:8510e5b3d670326326c5c1d4cb657cc66832193fe5d5b7015a51c7b1e1b1bf42"}, + {url = "https://files.pythonhosted.org/packages/a3/1d/b23984c05e39ddab35bbba33a3828dc4f896250220dcbd946c0fcad1e934/starlette-0.23.1-py3-none-any.whl", hash = "sha256:ec69736c90be8dbfc6ec6800ba6feb79c8c44f9b1706c0b2bb27f936bcf362cc"}, +] +"telemetrix-aio 1.11" = [ + {url = "https://files.pythonhosted.org/packages/8f/49/6df494d48bde51bc493de307c019ddbe2a95daa36e39c17f5985329c4170/telemetrix_aio-1.11-py2.py3-none-any.whl", hash = "sha256:815211f0320841c594e9ff623c455bbdc10656a8ffb45112c8de012a2b7e65cc"}, + {url = "https://files.pythonhosted.org/packages/f9/f2/c4baf39f4257145abdbaf2f12d8e9f96f64c3d604aca1603773b14786c6f/telemetrix-aio-1.11.tar.gz", hash = "sha256:2f1afe3293b580d0744f7d72ab8129cc3f457d08b5bb19740b18a178d9e14035"}, +] +"uvicorn 0.20.0" = [ + {url = "https://files.pythonhosted.org/packages/95/3c/9f4650ff609370456f796bb96a355dcddef1ded67e05d1b4eb3481088329/uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, + {url = "https://files.pythonhosted.org/packages/96/f3/f39ac8ac3bdf356b4934b8f7e56173e96681f67ef0cd92bd33a5059fae9e/uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, +]
--- a/pyproject.toml Sun Feb 05 14:05:06 2023 -0800 +++ b/pyproject.toml Sun Feb 05 14:06:19 2023 -0800 @@ -6,8 +6,12 @@ {name = "Drew Perttula", email = "drewp@bigasterisk.com"}, ] dependencies = [ - "pyfirmata>=1.1.0", + "asyncio-mqtt>=0.16.1", "paho-mqtt>=1.6.1", + "pyserial-asyncio>=0.6", + "starlette>=0.23.1", + "telemetrix-aio>=1.11", + "uvicorn>=0.20.0", ] requires-python = ">=3.10" license = {text = "MIT"}