digitalaudioswitch/main.py

195 lines
6.7 KiB
Python
Raw Normal View History

2023-05-17 22:10:49 +00:00
from machine import Pin, SPI, SoftI2C
import framebuf
2023-05-18 03:23:13 +00:00
import json
import machine
import network
import ubinascii
import utime
from umqtt.simple import MQTTClient
2023-05-17 22:10:49 +00:00
import ssd1306
import mcp4
2023-05-18 18:38:36 +00:00
from statetree import StateTree
2023-05-17 22:10:49 +00:00
2023-05-18 20:04:57 +00:00
VOLUME_MAX = const(128)
2023-05-18 03:23:13 +00:00
2023-05-18 20:04:57 +00:00
MQTT_KEEPALIVE = const(60)
MQTT_UPDATE_INTERVAL = const(60)
2023-05-18 03:23:13 +00:00
2023-05-18 18:38:36 +00:00
state = StateTree(
{
"network": "OFF",
"volume": {
"left": 0,
"right": 0,
},
2023-05-18 03:23:13 +00:00
}
2023-05-18 18:38:36 +00:00
)
2023-05-18 03:23:13 +00:00
last_update = 0
2023-05-17 22:10:49 +00:00
i2c = SoftI2C(sda=Pin(2), scl=Pin(16))
2023-05-18 20:04:57 +00:00
oled_width = const(128)
oled_height = const(32)
2023-05-17 22:10:49 +00:00
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
buf = bytearray((oled_height // 8) * oled_width)
fbuf = framebuf.FrameBuffer1(buf, oled_width, oled_height)
2023-05-18 03:23:13 +00:00
sta_if = network.WLAN(network.STA_IF)
2023-05-17 22:10:49 +00:00
spi = SPI(1)
cs = Pin(15, mode=Pin.OUT, value=1)
pot = mcp4.MCP4(spi, cs)
2023-05-18 03:23:13 +00:00
with open("settings.json", "r") as f:
settings = json.load(f)
mqtt = None
mqtt_client_id = ubinascii.hexlify(machine.unique_id())
mqtt_broker = settings["mqtt"]["broker"]
mqtt_prefix = settings["mqtt"]["prefix"]
def on_message(topic, msg):
2023-05-18 16:37:53 +00:00
print(f"MQTT <- [{topic}] {msg}")
2023-05-18 03:23:13 +00:00
try:
msg = json.loads(msg)
except:
return
if volume := msg.get("volume"):
if isinstance(volume.get("left"), int):
pot.write(0, volume["left"])
if isinstance(volume.get("right"), int):
pot.write(1, volume["right"])
2023-05-17 22:10:49 +00:00
2023-05-18 03:23:13 +00:00
def loop():
2023-05-18 18:38:36 +00:00
global mqtt, state, last_update
2023-05-18 03:23:13 +00:00
2023-05-18 18:38:36 +00:00
state["volume"]["left"] = pot.read(0)
state["volume"]["right"] = pot.read(1)
2023-05-18 03:23:13 +00:00
if not sta_if.active():
2023-05-18 16:37:53 +00:00
print("Connecting to WiFi")
2023-05-18 03:23:13 +00:00
sta_if.active(True)
sta_if.connect(settings["wifi"]["ssid"], settings["wifi"]["password"])
2023-05-18 16:37:53 +00:00
if sta_if.active() and not sta_if.isconnected():
2023-05-18 18:38:36 +00:00
state["network"] = "ACT"
2023-05-18 03:23:13 +00:00
if sta_if.isconnected():
2023-05-18 18:38:36 +00:00
if state["network"] != "OK":
2023-05-18 16:37:53 +00:00
ip, _, _, _ = sta_if.ifconfig()
print(f"WIFI Connected to {sta_if.config('ssid')}")
print(f"IP Address: {ip}")
2023-05-18 18:38:36 +00:00
state["network"] = "OK"
2023-05-18 03:23:13 +00:00
if not mqtt:
2023-05-18 16:37:53 +00:00
print("Starting MQTT client")
2023-05-18 03:23:13 +00:00
mqtt = MQTTClient(mqtt_client_id, mqtt_broker, keepalive=MQTT_KEEPALIVE)
mqtt.set_callback(on_message)
mqtt.set_last_will(f"{mqtt_prefix}/status", b"offline", retain=True)
2023-05-18 03:23:13 +00:00
mqtt.connect()
mqtt.subscribe(f"{mqtt_prefix}/set")
mqtt_device = {
"identifiers": mqtt_client_id,
"manufacturer": "correl",
"model": "digital-audio-switch",
"name": "Digital Audio Switch",
}
mqtt.publish(
f"homeassistant/number/digital-audio-switch/volume-left/config".encode(),
json.dumps(
{
"name": "Digital Audio Switch Volume (Left)",
"command_topic": f"{mqtt_prefix}/set",
"command_template": '{"volume": {"left": {{value}}}}',
"state_topic": f"{mqtt_prefix}/state",
"value_template": "{{ value_json.volume.left }}",
"availability_topic": f"{mqtt_prefix}/status",
"min": 0,
"max": VOLUME_MAX,
"mode": "slider",
"step": 1,
"unique_id": "digital-audio-switch-volume-left",
"device": mqtt_device,
}
).encode(),
retain=True,
)
mqtt.publish(
f"homeassistant/number/digital-audio-switch/volume-right/config".encode(),
json.dumps(
{
"name": "Digital Audio Switch Volume (Right)",
"command_topic": f"{mqtt_prefix}/set",
"command_template": '{"volume": {"right": {{value}}}}',
"state_topic": f"{mqtt_prefix}/state",
"value_template": "{{ value_json.volume.right }}",
"availability_topic": f"{mqtt_prefix}/status",
"min": 0,
"max": VOLUME_MAX,
"mode": "slider",
"step": 1,
"unique_id": "digital-audio-switch-volume-right",
"device": mqtt_device,
}
).encode(),
retain=True,
)
mqtt.publish(
f"homeassistant/number/digital-audio-switch/volume-master/config".encode(),
json.dumps(
{
"name": "Digital Audio Switch Volume (Master)",
"command_topic": f"{mqtt_prefix}/set",
"command_template": '{"volume": {"right": {{value}}, "left": {{value}}}}',
"state_topic": f"{mqtt_prefix}/state",
"value_template": """
{%set values = value_json.volume.left,
value_json.volume.right %}
{{ values|max }}
""",
"availability_topic": f"{mqtt_prefix}/status",
"min": 0,
"max": VOLUME_MAX,
"mode": "slider",
"step": 1,
"unique_id": "digital-audio-switch-volume-master",
"device": mqtt_device,
}
).encode(),
retain=True,
)
2023-05-18 18:38:36 +00:00
if state.changed or utime.time() - last_update >= MQTT_UPDATE_INTERVAL:
topic = f"{mqtt_prefix}/state".encode()
payload = json.dumps(state.dictionary).encode()
2023-05-18 16:37:53 +00:00
print(f"MQTT -> [{topic}] {payload}")
2023-05-18 18:38:36 +00:00
mqtt.publish(f"{mqtt_prefix}/status", b"online", retain=True)
mqtt.publish(topic, payload, retain=True)
2023-05-18 03:23:13 +00:00
last_update = utime.time()
mqtt.check_msg()
2023-05-18 18:38:36 +00:00
if state.changed:
oled.fill(0)
2023-05-18 20:04:57 +00:00
oled.framebuf.rect(10, 0, 92, 8, 1)
oled.framebuf.rect(
12, 2, round(state["volume"]["left"] / VOLUME_MAX * 88), 4, 1, True
)
oled.framebuf.rect(10, 10, 92, 8, 1)
oled.framebuf.rect(
12, 12, round(state["volume"]["right"] / VOLUME_MAX * 88), 4, 1, True
)
oled.text("L", 0, 0)
oled.text("R", 0, 10)
oled.text(f"{state['volume']['left']:3d}", 104, 0)
oled.text(f"{state['volume']['right']:3d}", 104, 10)
oled.text(f"WiFi: {state['network']}", 0, 20)
2023-05-18 18:38:36 +00:00
oled.show()
state.clean()
2023-05-17 22:10:49 +00:00
2023-05-18 03:23:13 +00:00
while True:
loop()
utime.sleep(1)