Skip to content

Instantly share code, notes, and snippets.

@fieldOfView
Created June 13, 2025 13:08
Show Gist options
  • Save fieldOfView/82d70d94979894e2355bff387090f924 to your computer and use it in GitHub Desktop.
Save fieldOfView/82d70d94979894e2355bff387090f924 to your computer and use it in GitHub Desktop.
MIDI to Tasmota PWM frequency script
import argparse
import time
import mido
import requests
class MidiToTasmota:
def __init__(self, midi_filename: str, ip_address: str, http_username: str|None=None, http_password: str|None=None):
self.midi_filename = midi_filename
if not http_username:
self._tasmota_url = f"http://{ip_address}"
elif not http_password:
self._tasmota_url = f"http://{http_username}@{ip_address}"
else:
self._tasmota_url = f"http://{http_username}:{http_password}@{ip_address}"
self._turned_on: bool = False
def _midi_to_frequency(self, midi_note) -> int:
"""Convert a MIDI note number to its corresponding frequency in Hz."""
return int(440 * (2 ** ((midi_note - 69) / 12)))
def _send_commands(self, commands: list[tuple[str, str]]) -> None:
"""Format one command, or append multuple in one Backlog command"""
if len(commands) == 1:
formatted_command: str = f"{commands[0][0]}+{commands[0][1]}"
else:
formatted_command: str = f"Backlog%20{'%3b'.join([f'{c[0]}%20{c[1]}' for c in commands])}"
requests.get(f'{self._tasmota_url}/cm?cmnd={formatted_command}')
def play(self) -> None:
"""Load a MIDI file and convert all notes to frequencies."""
midi = mido.MidiFile(self.midi_filename)
for track in midi.tracks:
for msg in track:
time.sleep(msg.time / 4000)
if msg.type == 'note_on':
freq = self._midi_to_frequency(msg.note)
print(f"Time {msg.time:4d}: MIDI {msg.note:3d}{freq:.2f} Hz")
commands: list[tuple[str, str]] = [('PWMFrequency', str(freq))]
if not self._turned_on:
commands.append(('Power', '1'))
self._turned_on = True
self._send_commands(commands)
def close(self) -> None:
"""Turn off the bulb if it was left on"""
self._send_commands([('Power', '0')])
self.turned_on = False
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Process MIDI file and send commands to Tasmota")
parser.add_argument("midi_filename", help="MIDI file to play")
parser.add_argument("ip_address", help="Tasmota device IP address")
parser.add_argument("--username", "--http-username", "-u", dest="http_username",
help="HTTP username for authentication (optional)")
parser.add_argument("--password", "--http-password", "-p", dest="http_password",
help="HTTP password for authentication (optional)")
args = parser.parse_args()
midi_to_tasmota = MidiToTasmota(
midi_filename=args.midi_filename,
ip_address=args.ip_address,
http_username=args.http_username,
http_password=args.http_password
)
try:
midi_to_tasmota.play()
except KeyboardInterrupt:
pass
midi_to_tasmota.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment