Uygulamalı Eğitimler

Akıllı Ev için Topic Tasarımı: Pratik bir MQTT Düzeni

Akıllı evler için pratik bir MQTT topic düzeni: odalar ve cihazlar, state ve komut topic'leri, retained mesajlar, erişilebilirlik, Home Assistant discovery ve ACL'ler.

Güncellendi · 9 dk okuma

Akıllı Ev için Topic Tasarımı: Pratik bir MQTT Düzeni

İyi bir akıllı ev MQTT topic düzeni sıkıcı ve öngörülebilirdir: oda başına bir dal, cihaz başına bir dal ve state, komutlar ve erişilebilirlik için sabit bir uç topic seti. Bu makale kopyalayabileceğiniz somut bir düzen öneriyor; komutların ve state'in neden ayrı topic'lerde durduğunu, hangi mesajların retained olması gerektiğini ve düzenin Home Assistant discovery ile broker ACL'lerine nasıl karşılık geldiğini açıklıyor.

Düzen
home/<room>/<device>/state          current state, JSON, retained
home/<room>/<device>/set            commands to the device, NOT retained
home/<room>/<device>/availability   "online" / "offline", retained, Last Will
home/<room>/<device>/event          one-off events (button press), NOT retained

İsimlendirme kuralları

Topic isimleri evinizin API'sinin bir parçasıdır. Sonradan değiştirmek her otomasyona dokunmak anlamına gelir; bu yüzden kuralları bir kez belirleyin:

  • Küçük harf ASCII, kelimeler - ile birleşik: living-room, ceiling-light. Boşluk, Türkçe karakter veya emoji yok.
  • Başta eğik çizgi yok. /home/kitchen boş bir ilk seviye oluşturur ve home/# ile eşleşmez.
  • Asla $ ile başlamayın: bu prefix $SYS gibi broker topic'lerine ayrılmıştır.
  • Yerler ve nesneler için isimler, uçlar için sabit kelimeler: state, set, availability, event. Cihazlar arasında status, cmd ve command karıştırmayın.
  • Derinliği sabit tutun. Her cihaz tam olarak home/<room>/<device> konumundadır; böylece home/+/+/state her zaman "tüm cihazların state'i" anlamına gelir.

Daha genel öneriler için MQTT topic tasarımı en iyi uygulamaları yazısına bakın.

Odaya göre mi, ID'ye göre mi?

StilÖrnekUygun olduğu durumDezavantajı
Odaya görehome/kitchen/ceiling-light/stateTipik evler, elle yazılan otomasyonlarCihazı taşımak topic'lerini değiştirir
ID'ye görehome/device/shelly-a8032a/stateBüyük veya sık değişen kurulumlarOda bilgisi başka yerde tutulmalı (metadata, HA alanları)
Karmahome/kitchen/shelly-a8032a/stateOkunabilir gruplamayla benzersiz isimlerTaşındığında yine değişir

Çoğu evde okunabilirlik açısından odaya göre düzen kazanır. Yer değiştiren cihazlarınız varsa (taşınabilir bir ısıtıcı, bir arabadaki sensör), onlara sabit bir ID verin ve home/mobile/ gibi nötr bir dalın altına koyun.

Komutlar ve state

En önemli kural: cihaz state'i yayınlar, diğer herkes komut yayınlar ve ikisi asla aynı topic'i paylaşmaz.

Uçtan uca bir açma işlemi
app      ──PUBLISH home/kitchen/ceiling-light/set   {"state":"ON"}            (not retained)
light    ──applies the command, then
light    ──PUBLISH home/kitchen/ceiling-light/state {"state":"ON","brightness":255}  (retained)
app, HA  ◀─ receive the new state and update the UI

Neden ayırmalı?

  • State topic'i her zaman gerçeği yansıtır. Komut başarısız olursa (röle takılır, cihaz çevrimdışıdır) state topic'i yine doğruyu gösterir ve arayüz yalan söylemez.
  • Yankı döngüsü olmaz. Kendi state topic'ine abone olan bir cihaz kendi güncellemelerini alır. set ve state ayrı olduğunda her taraf yalnızca ihtiyaç duyduğu topic'e abone olur.
  • Güvenli ACL'ler. Bir dashboard kullanıcısına .../set yazma ve .../state okuma izni verilebilir; ama cihazın state'ini taklit edemez.

Cihaz, hiçbir şey değişmemiş olsa bile yeni state'i komutu uyguladıktan sonra yayınlamalıdır. Arayüzlerin ve otomasyonların beklediği onay budur. Korelasyon ID'leri ile istek/yanıt desenleri için MQTT üzerinden komut ve kontrol yazısına bakın.

Neler retained olmalı

TopicRetained?Neden
.../stateEvetSonradan açılan bir dashboard güncel state'i anında gösterir
.../availabilityEvetYeni aboneler hangi cihazların çevrimdışı olduğunu bilir
.../setHayırRetained bir komut her yeniden bağlantıda tekrar oynatılır: elektrik kesintisinden sonra hayalet açılmalar
.../eventHayır"Düğmeye basıldı" yalnızca olduğu anda anlamlıdır
homeassistant/.../configEvetDiscovery yapılandırmaları Home Assistant yeniden başlatmalarından sonra da kalmalıdır

Bir komut yanlışlıkla retained olarak gönderildiyse, aynı topic'e boş bir retained mesaj yayınlayarak temizleyin. Ayrıntılar için retained mesajlar.

Last Will ile erişilebilirlik

Her cihaz bağlanırken availability topic'ine bir Last Will ayarlar: payload offline, retained, QoS 1. Bağlandıktan hemen sonra online mesajını retained olarak yayınlar. Cihaz gücünü veya Wi-Fi'ını kaybederse broker, keep alive süresi dolduğunda (yaklaşık 1,5 × keep alive aralığı) offline yayınlar. Bkz. MQTT Last Will.

40 satırda bir cihaz

Bu Python script'i (paho-mqtt 2.x), düzeni izleyen kısılabilir bir lamba gibi davranır. Örnekler, paylaşılan bir test endpoint'inde güvenle çalışabilmeleri için testmqtt/ prefix'ini kullanıyor; kendi evinizde bunu kaldırırsınız.

fake_light.py
import json, os
import paho.mqtt.client as mqtt

BASE = "testmqtt/home/kitchen/ceiling-light"
state = {"state": "OFF", "brightness": 0}

def publish_state(client):
    client.publish(f"{BASE}/state", json.dumps(state), qos=1, retain=True)

def on_connect(client, userdata, flags, reason_code, properties):
    client.publish(f"{BASE}/availability", "online", qos=1, retain=True)
    client.subscribe(f"{BASE}/set", qos=1)
    publish_state(client)

def on_message(client, userdata, msg):
    try:
        cmd = json.loads(msg.payload)
    except ValueError:
        cmd = {"state": msg.payload.decode(errors="replace").strip().upper()}
    if not isinstance(cmd, dict):
        cmd = {}
    if cmd.get("state") in ("ON", "OFF"):
        state["state"] = cmd["state"]
    if isinstance(cmd.get("brightness"), int):
        state["brightness"] = max(0, min(255, cmd["brightness"]))
    publish_state(client)  # always confirm with the real state

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="kitchen-ceiling-light")
if os.getenv("MQTT_USER"):
    client.username_pw_set(os.environ["MQTT_USER"], os.getenv("MQTT_PASS"))
client.will_set(f"{BASE}/availability", "offline", qos=1, retain=True)
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_forever()

State'i on_connect içinde yayınlamak önemlidir: her yeniden bağlantıdan sonra retained state yenilenir ve broker oturum tutmamış olsa bile abonelikler yeniden oluşturulur.

Düzeni test edin

Bir terminalde tüm cihazların state ve erişilebilirlik bilgisini izleyin:

Terminal 1 — izleme
mosquitto_sub -h broker.connectmq.com -p 1883 -v \
  -t 'testmqtt/home/+/+/state' -t 'testmqtt/home/+/+/availability'

Diğerinden komut gönderin:

Terminal 2 — komut
mosquitto_pub -h broker.connectmq.com -p 1883 -q 1 \
  -t 'testmqtt/home/kitchen/ceiling-light/set' -m '{"state":"ON","brightness":180}'

mosquitto_pub -h broker.connectmq.com -p 1883 -q 1 \
  -t 'testmqtt/home/kitchen/ceiling-light/set' -m 'off'

Ardından düzenin vaat ettiği davranışları kontrol edin:

  1. Her komut tam olarak bir state mesajı üretir.
  2. Terminal 1'i yeniden başlatın: güncel state ve online anında görünür (retained).
  3. Script'i kill -9 ile sonlandırın: keep alive zaman aşımından sonra offline görünür.
  4. Script'i yeniden başlatın: hiçbir komut tekrar oynatılmaz, çünkü set hiçbir zaman retained değildi.

Aynısını tarayıcıda online MQTT istemcisi ile yapabilir, herhangi bir abonelik desenini topic'lerinize karşı topic matcher ile kontrol edebilirsiniz.

İşe yarar abonelikler

İhtiyaçAbonelik
Evdeki her şeyhome/#
Mutfaktaki her şeyhome/kitchen/#
Tüm cihazların state'ihome/+/+/state
Hangi cihazlar çevrimdışıhome/+/+/availability
Tüm düğme basışlarıhome/+/+/event

Derinlik sabit olduğu için tek seviyeli + wildcard'ları yeterlidir; hata ayıklama dışında #'e nadiren ihtiyaç duyarsınız. Bkz. MQTT wildcard'larına derinlemesine bakış.

Home Assistant discovery

Home Assistant, homeassistant/<component>/<object_id>/config altındaki retained yapılandırma mesajlarından otomatik olarak entity oluşturabilir. Düzen doğrudan discovery alanlarına karşılık gelir:

homeassistant/switch/kitchen_ceiling_light/config (retained)
{
  "name": "Kitchen ceiling light",
  "unique_id": "kitchen_ceiling_light",
  "state_topic": "home/kitchen/ceiling-light/state",
  "value_template": "{{ value_json.state }}",
  "command_topic": "home/kitchen/ceiling-light/set",
  "payload_on": "ON",
  "payload_off": "OFF",
  "availability_topic": "home/kitchen/ceiling-light/availability"
}

payload_available ve payload_not_available varsayılan olarak online ve offline değerlerini alır; yukarıdaki Last Will'in yayınladığı da tam olarak budur. Entegrasyonun tam kurulumu için Home Assistant ve MQTT yazısına bakın.

Düzeni izleyen ACL'ler

Tutarlı bir ağaç, izinleri kısa tutar:

İstemciYazmaOkuma
Her cihazkendi state, availability, event topic'lerikendi set topic'i
Home Assistant / otomasyon merkezihome/+/+/set, homeassistant/#home/#
Duvar tableti dashboard'uhome/+/+/sethome/+/+/state, home/+/+/availability
Misafir veya izleme kullanıcısıhiçbir şeyhome/+/+/availability

Cihaz başına bir kimlik bilgisi, ele geçirilmiş bir akıllı prizin yalnızca kendisi hakkında yalan söyleyebileceği, ama ön kapı kilidinizi açamayacağı anlamına gelir.

Bu düzeni benimsemenin bir maliyeti yoktur ve her yeni cihazda karşılığını verir: sabit derinlik, ayrı state ve set, retained state ve erişilebilirlik, asla retained olmayan komutlar. Kuralları yazıya dökün, her yeni cihazı yukarıdaki dört kontrolle test edin; eviniz büyüdükçe otomasyonlarınız çalışmaya devam eder.

Sıkça sorulan sorular

MQTT komut topic'leri retained olmalı mı?

Hayır. Retained bir komut, cihaz her yeniden bağlandığında ona tekrar iletilir; böylece bir lamba elektrik kesintisinden sonra kendiliğinden yanabilir. State ve erişilebilirliği retained tutun, komutları retain bayrağı olmadan gönderin.

Oda topic'in bir parçası olmalı mı?

Tipik bir evde evet: home/kitchen/ceiling-light kolay okunur ve home/kitchen/# ile tüm odaya abone olmanızı sağlar. Dezavantajı, bir cihazı taşımanın topic'lerini değiştirmesidir. Büyük kurulumlar genellikle sabit cihaz ID'leri kullanır ve odayı metadata olarak tutar.

Tek bir JSON state topic'i mi, yoksa her özellik için ayrı topic mi?

Cihaz başına tek bir JSON state mesajını tutarlı tutmak daha kolaydır ve Home Assistant'ın JSON şablonlarının beklediği de budur. Özellik başına topic'ler çok küçük sensörler ve bazı araçlar için daha basittir. Bir stil seçin ve tüm cihazlarda onu kullanın.