Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Analog Terminal Bell Support #428

Closed
wants to merge 1 commit into from

Conversation

tenderlove
Copy link

This PR adds support for the Analog Terminal Bell. Have you ever made a mistake in your shell only to notice that you accidentally missed the terminal bell notification? Never miss a terminal bell notification again with the Analog Terminal Bell!

Here is a short video explaining the Analog Terminal Bell:

Analog Terminal Bell

This patch adds Analog Terminal Bell support to iTerm2.

(Note: this PR is just a joke. Adding support would be awesome, but I just made this PR as a way to make people smile. Also I have no idea how to correctly do macOS development, so this PR is probably not the best 😅)

I really have no idea what I'm doing, this is the first time I've
modified a real app for macOS
@robbiet480
Copy link

robbiet480 commented Sep 8, 2020

I get that this is a joke, but you just gave me an idea to bake the bell into QMK for those with a buzzer/speaker on their keyboards. Might this PR be modified slightly to instead allow iTerm2 to call a command for bell? That way it could be a script to ring the Analog Terminal Bell (via GPIO) or play a tone on your keyboard (via HID to QMK). Bonus would be not having to ship HID support in iTerm directly.

@tenderlove
Copy link
Author

Might this PR be modified slightly to instead allow iTerm2 to call a command for bell?

Ya, I was thinking that if we wanted to take this patch seriously, maybe adding a way to plug in to the bell logic would make sense. I don't know the best way to do that though. My fear is that shelling out would be too slow, but the code that I wrote for this patch is extremely inefficient (it looks up the USB device on every bell activation!) and as you can see from the video the response time was fine.

(Anyway, option specifically for the Analog Terminal Bell ™ is definitely not the best 😆)

@gnachman
Copy link
Owner

gnachman commented Sep 13, 2020

LOL this is awesome. If you sold these I would definitely buy one.

I think adding a bell interface to the Python API is a somewhat saner way of adding this feature.

Commit cbb7c96 adds a bellCount variable to Session, which you can use to detect when the bell rings. Here's a Python script that prints "ding", which you can replace with GPIO twiddling as you see fit 😆

You'll need the current build from master or tomorrow's nightly build (due out in about 24 hours because I missed the deadline).

#!/usr/bin/env python3.7

import iterm2

async def main(connection):
    app = await iterm2.async_get_app(connection)

    async def my_task(session_id):
        async with iterm2.VariableMonitor(connection, iterm2.VariableScopes.SESSION, "bellCount", session_id) as mon:
            while True:
                await mon.async_get()
                print("ding")

    await (iterm2.EachSessionOnceMonitor.
        async_foreach_session_create_task(app, my_task))

iterm2.run_forever(main)

@gnachman gnachman closed this Sep 13, 2020
@tenderlove
Copy link
Author

@gnachman LOL!! Thank you so much!!!

@tenderlove tenderlove deleted the analog-terminal-bell branch September 16, 2020 14:34
@tenderlove tenderlove restored the analog-terminal-bell branch September 16, 2020 14:34
@zvakanaka
Copy link

After installing the script (see bottom) in the standard iTerm (not nightly or latest build), with hidapi and PyMCP2221A as dependencies, the solenoid clicked (line 10 success). But, the terminal bell wasn't triggering it, so I updated to the latest nightly iTerm build. It cannot install hidapi in Python 3.8 (install error). So I set the script to use Python 3.7, which was able to install hidapi and PyMCP2221A, but then the script does not run because this error happens (Scripts > Manage > Console):

10/5, 7:32:52.964 PM: /bin/zsh -c /Applications/iTerm.app/Contents/Resources/it2_api_wrapper.sh /Users/adam/Library/Application\ Support/iTerm2/Scripts/Test/iterm2env/versions/3.7.5/bin/python3 /Users/adam/Library/Application\ Support/iTerm2/Scripts/Test/Test/Test.py
10/5, 7:32:52.972 PM: + unset PYTHONPATH
10/5, 7:32:52.972 PM: + export PYTHONUNBUFFERED=1
10/5, 7:32:52.972 PM: + PYTHONUNBUFFERED=1
10/5, 7:32:52.972 PM: + '/Users/adam/Library/Application Support/iTerm2/Scripts/Test/iterm2env/versions/3.7.5/bin/python3' '/Users/adam/Library/Application Support/iTerm2/Scripts/Test/Test/Test.py'
10/5, 7:32:53.333 PM: Traceback (most recent call last):
10/5, 7:32:53.333 PM:   File "/Users/adam/Library/Application Support/iTerm2/Scripts/Test/Test/Test.py", line 4, in <module>
10/5, 7:32:53.333 PM:     from PyMCP2221A import PyMCP2221A
10/5, 7:32:53.333 PM: ModuleNotFoundError: No module named 'PyMCP2221A'
10/5, 7:32:53.361 PM: 
10/5, 7:32:53.361 PM: ** Script exited with status 1 **
#!/usr/bin/env python3.7

import iterm2
from PyMCP2221A import PyMCP2221A
import time

gpio = PyMCP2221A.PyMCP2221A()
gpio.GPIO_Init()
gpio.GPIO_0_OutputMode()
gpio.GPIO_0_Output(0)
                
async def main(connection):
    app = await iterm2.async_get_app(connection)

    async def my_task(session_id):
        async with iterm2.VariableMonitor(connection, iterm2.VariableScopes.SESSION, "bellCount", session_id) as mon:
            while True:
                await mon.async_get()
                gpio.GPIO_0_Output(1)
                time.sleep(.1)
                gpio.GPIO_0_Output(0)

    await (iterm2.EachSessionOnceMonitor.
        async_foreach_session_create_task(app, my_task))

iterm2.run_forever(main)

We are so close!

@zvakanaka
Copy link

It's hacky, but here's a workaround to start up a Python HTTP server outside of the iTerm script, then have the iTerm script send a request to it at every bell.

Server:

from http.server import BaseHTTPRequestHandler, HTTPServer
from PyMCP2221A import PyMCP2221A
import time

hostName = "localhost"
serverPort = 8080

gpio = PyMCP2221A.PyMCP2221A()
gpio.GPIO_Init()
gpio.GPIO_0_OutputMode()
gpio.GPIO_0_Output(0)

class MyServer(BaseHTTPRequestHandler):
    def do_GET(self):
        gpio.GPIO_0_Output(1)
        time.sleep(.1)
        gpio.GPIO_0_Output(0)

        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(bytes("<html><head><title>Ring the Bell</title></head>", "utf-8"))
        self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
        self.wfile.write(bytes("<body>", "utf-8"))
        self.wfile.write(bytes("<p>The bell has rang.</p>", "utf-8"))
        self.wfile.write(bytes("</body></html>", "utf-8"))

if __name__ == "__main__":
    webServer = HTTPServer((hostName, serverPort), MyServer)
    print("Server started http://%s:%s" % (hostName, serverPort))

    try:
        webServer.serve_forever()
    except KeyboardInterrupt:
        pass

    webServer.server_close()
    print("Server stopped.")

Install dependencies: $ pip3 install hidapi PyMCP2221A
Run: $ python3 server.py

iTerm Script:

#!/usr/bin/env python3

import iterm2
import requests

async def main(connection):
    app = await iterm2.async_get_app(connection)

    async def my_task(session_id):
        async with iterm2.VariableMonitor(connection, iterm2.VariableScopes.SESSION, "bellCount", session_id) as mon:
            while True:
                await mon.async_get()
                
                x = requests.get('http://localhost:8080/')
                print(x.status_code)

    await (iterm2.EachSessionOnceMonitor.
        async_foreach_session_create_task(app, my_task))

iterm2.run_forever(main)

Add requests as a dependency of the iTerm script.

@gnachman
Copy link
Owner

gnachman commented Oct 7, 2020

I filed trezor/cython-hidapi#95. If that doesn't work out someone should fork hidapi and add -Wno-error to CFLAGS in setup.py, in the spirit of low-effort hackery. I'd do it but it would require a lot of paperwork at the office.

@gnachman
Copy link
Owner

gnachman commented Oct 8, 2020

Following the rabbit hole, now we need to get libusb/hidapi#196 fixed.

@gnachman
Copy link
Owner

libusb/hidapi#198 closes the hidapi issue. Now just waiting for cython-hidapi to pull it in.

@gnachman
Copy link
Owner

@tenderlove cython-hidapi has bumped the hidapi version in their master. Want to try again?

@gnachman
Copy link
Owner

FYI trying to build and install into Application Support is a nightmare because Python gets confused by the space in the directory name. Hopefully he bumps the pypi package soon and then it can just be installed by pip3 as usual.

@rufo
Copy link

rufo commented Feb 28, 2021

Just to follow up on this - I finally put together the parts I bought several months ago this current weekend, and with the changes above in place, it does totally work without any additional messing about 🎉

The script @zvakanaka posted basically does it, but with one tweak of changing the sleep time to .009 for a more satisfying ring.

Thanks to everyone for enabling this, it's quite fun 🛎️ 😄

@gnachman
Copy link
Owner

@rufo Awesome! Share a video!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants