diff --git a/nix/flake-module.nix b/nix/flake-module.nix
index e4924afd374c912aa5c4af52c1ed0c530e16faf0..39daa17da6b4407acb88eb0ff349a536a1de7f85 100644
--- a/nix/flake-module.nix
+++ b/nix/flake-module.nix
@@ -29,7 +29,8 @@ in
               bookmark url csquotes listings listings-ext sourcecodepro silence
               biblatex-ieee ly1 metafont transparent catchfile microtype
               l3kernel l3packages texcount moreverb pdfpages pdflscape
-              tabularray ninecolors;
+              tabularray ninecolors minted fvextra latex2pydata
+              newfloat pdftexcmds pgfkeyx pgfopts upquote lineno;
           });
         };
         document.font = mkOption {
@@ -63,7 +64,8 @@ in
           packages.document = pkgs.stdenvNoCC.mkDerivation {
             name = "latex-document";
             src = config.document.source;
-            buildInputs = [ pkgs.coreutils pkgs.inkscape config.document.texlive config.document.font locales ];
+            buildInputs = (with pkgs; [ coreutils inkscape latexminted ]) ++
+              [ config.document.texlive config.document.font locales ];
             phases = [ "unpackPhase" "buildPhase" "installPhase" ];
             buildPhase = ''
               set -o errexit
@@ -92,7 +94,7 @@ in
             let
               doc-watcher = pkgs.writeShellApplication {
                 name = "doc-watcher";
-                runtimeInputs = with pkgs; [ watchexec coreutils inkscape ] ++ [ config.document.texlive ];
+                runtimeInputs = with pkgs; [ watchexec coreutils inkscape latexminted ] ++ [ config.document.texlive ];
                 text = ''
                   export OSFONTDIR="${config.document.font}/share/fonts"
                   watchexec -r --print-events -- \
diff --git a/src/contents/5-elozmenyek.tex b/src/contents/5-elozmenyek.tex
index a2007807f5bcef5fa9d96a94b25939d9b70715e5..ad3213aae5a3ca3d73299905682eecbe62e8d9be 100644
--- a/src/contents/5-elozmenyek.tex
+++ b/src/contents/5-elozmenyek.tex
@@ -425,6 +425,7 @@ stabilitását és biztonságát a modern DIY rendszerek nyitottságával és
 rugalmasságával igyekszik ötvözni.
 
 \section{Biztonságtechnikai kérdések}
+\label{bizt-kerd}
 
 \paragraph{} Egy riasztórendszer megalkotásához elengedhetetlen a
 tervezőnek ismermie a biztonságtechnika alapjait, illetve tisztában lennie a
diff --git a/src/contents/6-tervezes.tex b/src/contents/6-tervezes.tex
index 62cbb70b9c6bf6eaf4cea9d9bf20ede794f79e42..2b0db55461cb44cadc63d4174eaa24f72b55417a 100644
--- a/src/contents/6-tervezes.tex
+++ b/src/contents/6-tervezes.tex
@@ -182,6 +182,7 @@ célhardverekhez tartom jobban igazodónak, nem magas szintű IoT-barát
 megoldásoknak.
 
 \subsection{Rust nyelv és környezet}
+\label{rust-env}
 
 \paragraph{} A Rust nyelv ötlete egy Mozilla-nál dolgozó fejlesztő fejéből
 pattant ki, amikor egy liftbe beszállt és az elromlott, hibás szoftver miatt.
@@ -323,6 +324,7 @@ implementálásakor, amivel potenciálisan azonnal a flash memóriába lehet maj
 framework; előre tervezés szempontjából hasznos tudásnak tartom.
 
 \subsection{Home Assistant okosotthon}
+\label{hass}
 
 \paragraph{} A Home Assistant egy nyílt platform, otthon-automatizációs
 célokra. Egy központi, egységes felületet ad a felhasználó számára, ahol
@@ -351,8 +353,9 @@ szeretnénk irányítani. A szoftver definiálja az eszköz (device) fogalmát;
 ami szintén egy entitások csoportosítása, de azt nem a felhasználó, hanem a
 gyártó/megalkotó határozza meg -- például egy IP kamera, ahol van egy kamera
 entitás, és egy nappali és éjszakai mód között váltó gomb. Vagy vegyünk egy okos
-kapu vezérlőt, ahol két mód elérhető: a kapu két szárnyát egyszerre nyitó gomb,
-csak az egyik szárnyat nyitó gomb, és akár egy állapot visszajelző szenzor.
+kapu vezérlőt, ahol az entitások a következők: a kapu két szárnyát egyszerre
+nyitó gomb, csak az egyik szárnyat nyitó gomb, és akár egy állapot visszajelző
+szenzor.
 
 Az entitásokat a felhasználó eléri a Home Assistant webes felületén (frontend),
 amiben azokra automatizmusokat is lehet létrehozni. Egy automatizmus három
@@ -377,22 +380,26 @@ ha nincs mozgás legalább 10 percen át, akkor kapcsolódjon le a lámpa.
 
 Az MQTT egy első osztályú integráció a Home Assistantban. Az MQTT minden QoS
 szintjét támogatja, illetve a többi integrációhoz képest sokkal általánosabb
-interfészt ad. Ez kimagaslóan jobb élményt ad a végfelhasználó és a fejlesztő
-számára is. MQTT-n keresztül lehetőség van entitások és eszközök telepítésére
-manuális konfiguráció vagy az úgynevezett ``MQTT-discovery'' segítségével.
-Mindkét esetben az eszközöknek és entitásoknak van egy deklaratív leíró sémája,
-ami tartalmaz meta-adatokat az azok működtetésére. Manuális konfiguráció esetén
-ezt a Home Assistantban YAML formátumban kell megadni, autodiscovery során
-pedig egy előre meghatározott topic-on kell küldeni JSON formátumban a rendszer
-felé. Látható, hogy az utóbbi esetben az azt támogató eszköz végfelhasználói
-onbarding élménye sokkal kényelmeseb. Például, az eszköz első indítása után
-az autodiscovery üzenet küldésével a Home Assistant frontend felületén azonnal
-látni fog a felhasználó egy jóváhagyandó üzenetet, hogy az készen áll a
-használatra. Jóváhagyás után a meghirdetett entitások importálásra kerülnek a
-Home Assistantba. \cite{hass-mqtt} Célom úgy megvalósítani a saját rendszert,
-hogy az alapból támogassa az autodiscovery-t. Így \aref{kereskedelmi}.
-fejezetben megismert DIY rendszereknél is potenciálisan jobb élményt tudna
-nyújtani, hiszen a kommunikáció vezetékes médiumon történne. Ennek realitását
+interfészt ad. Ez kimagaslóan jobb élményt ad a végfelhasználó számára,
+mert egységes  és konzisztens marad a frontend. A fejlesztő számára is jobb
+élmény, mert az intgrációt így nem szükséges a Home Assistant forráskódján át
+implementálni Pythonban. MQTT-n keresztül lehetőség van entitások és eszközök
+telepítésére manuális konfigurációval vagy az úgynevezett ``MQTT-discovery''
+segítségével. Mindkét esetben az eszközöknek és entitásoknak van egy deklaratív
+leíró sémája, ami tartalmaz meta-adatokat és leírást azok működtetésére.
+Manuális konfiguráció esetén ezt a Home Assistantban YAML formátumban kell
+megadni, autodiscovery során pedig egy előre meghatározott MQTT topic-on kell
+küldeni JSON formátumban a rendszer felé. Látható, hogy az utóbbi esetben
+az azt támogató eszköz végfelhasználói onbarding élménye sokkal kényelmeseb.
+Például, az eszköz első indítása után az autodiscovery üzenet küldésével a Home
+Assistant frontend felületén azonnal látni fog a felhasználó egy jóváhagyandó
+üzenetet, hogy az készen áll a használatra. Jóváhagyás után a meghirdetett
+entitások importálásra kerülnek a Home Assistantba. \cite{hass-mqtt} Célom úgy
+megvalósítani a saját rendszert, hogy az alapból támogassa az autodiscovery-t.
+Így \aref{kereskedelmi}. fejezetben megismert DIY rendszereknél is potenciálisan
+jobb élményt tudna nyújtani, hiszen a kommunikáció vezetékes médiumon történne,
+ahol az Ethernet csatlakoztatásával azonnal a hálózatra kerül az eszköz. Ez
+elméletben kényelmesebb, mint például a WiFi konfigurációja. Ennek realitását
 szintén a gyakorlatban fogjuk tudni megállapítani.
 
 \clearpage % Ez azért kell, hogy nehogy képek átcsússzanak a következő fejezethez
diff --git a/src/contents/7-eredmenyek.tex b/src/contents/7-eredmenyek.tex
index 5da6996cba8bd8819c0fc78f364b2eda9ade68d6..3ef2a40088b5cbd972305180cced8d6b65c183ef 100644
--- a/src/contents/7-eredmenyek.tex
+++ b/src/contents/7-eredmenyek.tex
@@ -16,6 +16,59 @@
 }
 
 \section{Hardver}
+\label{hardware}
+
+\paragraph{} A megvalósítást a hardver megépítésével kezdtem. Legelőször a
+tápellátást helyeztem üzembe. Egy 12 voltos akkumulátort és hozzá egy töltés
+vezérlőt szereztem, melynek kimenetére egy buck-boost átalakító modult
+helyeztem. Így az akkumulátor feszültségét $3,3\ V$-ra tudtam illeszteni,
+az ESP32 számára. Ezután a fejlesztőpanel megfelelő lábaira kötöttem a
+perifériákat. A firmware fejlesztésének idejéig a PIR szenzorok helyett egyszerű
+nyomógombokat használtam. Ezzel könnyedén tudtam szimulálni a mozgás észlelését.
+Hasonlóan a sziréna kimenete helyett először egy LED-et hajtottam meg a
+vonallal, természetesen egy ellenálláson keresztül.
+
+\Aref{esp}. fejezetben megismert beépített RMII interfészt szerettem volna
+használni az Ethernet perifériához, melyet nem sikerült működésre bírni a
+tesztüzem során. A modul egy olcsó Wiznet IP101GRI interfészt használ, bár
+nem találtam hozzá hivatalos datasheet-et, csak az IP101 meghajtó chipről.
+\cite{ip101} A hiba forrását próbáltam több úton kideríteni, de sajnos nem
+találtam meg. Ellenőriztem a csatlakozások helyességét -- rendben voltak,
+a szoftveres használatot próbáltam újraimplementálni példák alapján --
+eredménytelenül, illetve másik gyártó által adott példaprogram sem működött.
+\cite{ip101-example} Ezzel arra tudtam következtetni, hogy maga az Ethernet
+vezérlővel volt probléma, így más utat kellett, hogy válasszak. Szerencsére a
+periféria az RMII interfészen kívül támogatott SPI buszon való kommunikációt
+is, úgyhogy ezzel próbálkoztam -- sikeresen. Viszont a nehézség itt az volt,
+hogy az sajátos protokollon kommunikált, melyhez szintén nem találtam sem
+drivert, sem annak működéséről dokumentációt. Azt a döntést hoztam, hogy a végső
+megvalósításban egy új, kizárólag SPI interfészt támogató perifériát használok,
+melyhez beépített támogatás van az ESP-IDF fejlesztőkörnyezetünkben. A Wiznet
+W5500 chip ezeknek megfelel és az ehhez választott perifériám ezen a chipen
+alapul. Innentől kedzve ezzel a megoldással dolgoztam. \cite{w5500-datasheet}
+
+Miután a szoftvert sikerült arra a szintre hozni, hogy a perifériákkal tudott
+a központi egységem kommunikálni, a rendszert elhelyeztem tesztüzem jelleggel
+az otthonomban, ahol onnantól kezdve távolról tudtam frissíteni a firmware-t
+-- melynek működéséről \aref{firmware}. fejezetben számolok be. Az átmenetileg
+elhelyezett nyomógombokat és LED-et eltávolítottam, és azokat a valós PIR
+szenzorokra és a szirénára kötöttem. Otthonomban ezelőtt korábban volt egy
+riasztórendszer telepítve, így abból újra fel tudtam használni ezeket a
+perifériákat. Az így kapott végleges hardveres bekötést \aref{diag:hardware}.
+ábrán rajzoltam le. A központi egység egy fém szerelődobozban került
+elhelyezésre, ahonnan minden vezeték a falon keresztül jut el a perifériákhoz,
+illetve a tápellátás sem könnyedén megszüntethető -- bár itt az elismerés az
+előző rendszer telepítőjét illeti, nem nyúltam hozzá a kábelezéshez, mivel
+\aref{bizt-kerd}. fejezet mechanikai biztonsági kérdései szerint megfelelőnek
+találtam. Első beszereléskor a tápellátás megszűntetésével kipróbáltam, hogy a
+rendszer jól működik-e önerőből -- valóban, elegendő volt az akkumulátor az új
+központi egység és a telepített perifériák meghajtásához.
+
+Reflektálva \aref{feladat}. fejezetben elvártakra, a rendszer hardveres
+felépítését -- apró korrigálás után -- sikeresen megalkottam. \Aref{bizt-kerd}.
+fejezetben definiált rendszer modelljére illeszkedik, annak biztonsági kérdéseit
+betartja. Ezek után a szoftverre koncentrálok; a riasztórendszer logikáját
+implementálom, majd az integrációkat.
 
 \begin{figure}[htbp!]
 	\ttfamily
@@ -26,7 +79,510 @@
 \end{figure}
 
 \section{Firmware}
+\label{firmware}
+
+A firmware forráskódját GitHub-on tettem közzé, mely az alábbi címen érhető el:
+\\
+\extlink{https://github.com/akosnad/rusty-esp-alarm}{akosnad/rusty-esp-alarm}
+
+\paragraph{} A szoftver fejlesztése előtt a fejlesztői környezetet alakítottam
+ki. A Rust beépített csomagkezelőjével (\textit{cargo}) \cite{cargo} létrehoztam
+az \textit{esp-idf-template} \cite{esp-idf-template} sablon alapján a
+mappastruktúrát. A sablonban előre vannak konfigurálva a környezethez bizonyos
+szükséges és kényelmi beállítások. Ezek közül megemlítendő:
+
+\begin{itemize}
+	\item Az \textit{esp-idf-svc} könyvtár függőségként fel van véve a projekthez
+	      a \verb|Cargo.toml| leíró fájlban.
+	      Ez a könyvtár tartalmazza \aref{esp}. és \ref{rust-env}. fejezetben
+	      látott ESP-IDF framework Rust oldali alacsony és magas szintű API-jait.
+	\item A Rust fordító és linker optimizációs szintje ``s'' értékre van állítva, ami az eredmény
+	      bináris méretének csökkentésére helyezi a hangsúlyt.
+	\item A framework által elvárt egyéb fordító és linker beállítások alkalmazva vannak
+	      a\\ \verb|.cargo/config.toml| fájlban.
+	\item Az \verb|sdkconfig.defaults| fájlban az ESP-IDF framework beállításai találhatóak,
+	      ehhez ésszerű alapértelmezéseket állít be.
+	\item A szokásos ``hello world'' példaprogramot tartalmazza a \verb|main.rs| fő forrásfájl.
+\end{itemize}
+
+Ezek után nekiláttam a perifériák vezérlésének implementálásához. A \verb|main|
+függvény valahány utasítása az a perifériák inicializálását végzi, utána az
+egyes feladatkörök elindítása több szálon (thread). A legnagyobb kihívás
+itt az ethernet vezérlő működésre bírása volt. Ahogy \aref{hardware}. fejezetben
+is említettem, végül a Wiznet W5500 típusú SPI interfész alapú chip vezérlését
+kellett implelentáljam. A fizikai réteg inicializálása egyszerűen történik, mely
+így néz ki a forráskódban:
+
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/main.rs\#L103}{main.rs}
+\begin{minted}[linenos,firstnumber=103]{rust}
+let eth = Box::leak(Box::new(esp_idf_svc::eth::EspEth::wrap(
+    esp_idf_svc::eth::EthDriver::new_spi(
+        SpiDriver::new(
+            peripherals.spi2,
+            pins.gpio18,
+            pins.gpio19,
+            Some(pins.gpio23),
+            &SpiDriverConfig::new().dma(Dma::Auto(4096)),
+        )?,
+        pins.gpio26,
+        Some(pins.gpio5),
+        Some(pins.gpio33),
+        esp_idf_svc::eth::SpiEthChipset::W5500,
+        20.MHz().into(),
+        Some(&[0x02, 0x00, 0x00, 0xfc, 0x18, 0x01]),
+        None,
+        sysloop.clone(),
+    )?,
+)?));
+\end{minted}
+
+Először egy \mintinline{rust}/SpiDriver/ -t hozok létre, ahol megadom, hogy
+milyen kivezetések felelnek meg az SCLK, MISO, MOSI vezetékeknek, illetve
+bekonfigurálom, hogy az interfész használjon egy automatikusan kiosztott DMA
+csatornát maximum 4096 szavas puffermérettel. A driver azonnal átadásra kerül az
+\mintinline{rust}/EthDriver/ inicializálásához, hiszen más eszköz nincsen az SPI
+buszon. Így a forráskódban sincs hozzáférés ezentúl a nyers SPI buszhoz, hanem
+azt csak a W5500 driver fogja tudni használni. A busz driveren kívül megadom a
+maradék W5500 chiphez specifikus kivezetések pin beosztását, a chip típusát, az
+SPI buszon üzemeltetett órajel frekvenciáját, illetve egy beégetett MAC címet.
+\cite{esp-idf-svc} Így egy szépen enkapszulált \mintinline{rust}/eth/ változónk
+van, mely reprezentálja az Ethernet interfészünket. Ezt a futás során később
+átadom a hálózat kezelésére lértehozott modulnak:
+
+\begin{minted}[linenos,firstnumber=216]{rust}
+// Network stack
+network::init(eth, sysloop.clone(), timer, status_tx.clone(), &mut tasks)?;
+\end{minted}
+
+A \mintinline{rust}/network/ modulban a hálózat teljeskörű kezelését az \mintinline{rust}/eth_task/ függvény végzi:
+
+%TC:ignore
+% texcountnak nem tetszik ez a minted env, hibát dob...
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/network.rs\#L60}{network.rs}
+\begin{minted}[linenos,firstnumber=60]{rust}
+async fn eth_task<T>(
+	mut eth: AsyncEth<&mut EspEth<'_, T>>,
+	status_tx: mpsc::Sender<StatusEvent>,
+) -> ! {
+	loop {
+		// ...
+\end{minted}
+%TC:endignore
+A logikája a következő lépésekből áll:
+\begin{enumerate}
+	\item A fizikai link kezelése: várakozás amíg nincs link, illetve annak megszűnésekor visszatérés ide
+	\item IP réteg kezelése: várakozás amíg a DHCP-n keresztüli konfiguráció megtörténik
+	\item MQTT kliens inicializálása, elindítása konkurrensen
+\end{enumerate}
+
+Láthatjuk, hogy a függvény az \mintinline{rust}/async/ kulcsszóval van
+ellátva, és egyes függvényhívások az \mintinline{rust}/.await/ operátor postfix
+notációval vannak írva. Rustban így lehet kooperatív többszálú programozást
+végezni. Igyekeztem ennek támogatását kihasználni az \textit{esp-idf-svc}
+könyvtárban. A legtöbb műveletből létezik blokkoló és nem blokkoló (async)
+változat. Az aszinkron megközelítés effektívebb tud lenni egy ilyen rendszernél,
+hiszen a kevés processzor erőforrást ki kell tudni minden pillanatban
+kihasználni -- a blokkoló műveletre várakozás helyett más feladatot futtatunk.
+A preemptív megközelítés is működik, ahol fix időbeosztás van a futás jogára,
+bár úgy gondolom egy ilyen rendszernél a teljes kontroll jobb ha nálunk van,
+hiszen a sűrű kontextusváltás is befolyásolhatja a teljesítményt kevés erőforrás
+mellett. Ezt az állításomat nem ellenőriztem a gyakorlatban, csupán így
+közelítettem meg az implementációt.
+
+Az MQTT kliens működését az \mintinline{rust}/mqtt_task/ és az utána következő segédfüggvények végzik.
+A fő eseményfeldolgozó ciklus így néz ki:
+
+%TC: ignore
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/network.rs\#L136}{network.rs}
+\begin{minted}[linenos,firstnumber=136]{rust}
+fn mqtt_task(
+    status_tx: mpsc::Sender<StatusEvent>,
+    mqtt_client_config: MqttClientConfiguration<'_>,
+) -> anyhow::Result<()> {
+    info!("Starting MQTT...");
+    let (client, mut connection) =
+        EspMqttClient::new_with_conn(MQTT_ENDPOINT, &mqtt_client_config)?;
+    let mut client = Some(client);
+    let mut ota = None;
+
+    while let Some(msg) = connection.next() {
+        match msg {
+			Err(e) => /* ... */,
+			Ok(msg) => {
+				// ...
+\end{minted}
+%TC: endignore
+
+Az \mintinline{rust}/msg/ változó az MQTT kliens fogadott eseményeit
+fogja tartalmazni, ami lehet a csatlakozás esemény, lecsatlakozás esemény,
+feliratkozott topic-ra érkezett üzenet. A \mintinline{rust}/handle_mqt_message/
+függvény kezeli a beérkezett üzeneteket feliratkozott topic-ra. Az eszköz
+lényegében egyetlen dologra kíváncsi: a firmware frissítésre -- Over the
+Air Update (OTA). Ezt egy erre dedikált MQTT topic-on tudja megkapni, az új
+firmware tartalmát ide lehet publikálni bináris formátumban. Ennek a kezelése a
+legbonyolultabb az egész firmware kódjában -- nem is az alapfeladat része, így
+csak egy kivonatát mutatom itt be:
+
+%TC: ignore
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/network.rs\#L222}{network.rs}
+\begin{minted}{rust}
+fn handle_ota_message(msg: MessageImpl, ota: &mut Option<OtaUpdate>) -> anyhow::Result<()> {
+    let data = msg.data();
+    if let Some(mut in_progress_ota) = ota.take() {
+        match msg.details() {
+            Details::InitialChunk(_) => {
+			    // hiba -- folyamatban lévő OTA közben érkezett egy újabb darabolt folyam első szelete
+                // ...
+            }
+            Details::SubsequentChunk(SubsequentChunkData {
+                current_data_offset,
+                total_data_size,
+            }) => {
+                // tovább írás a flash memóriára
+                // ...
+            }
+            Details::Complete => {
+                // ekkor a teljes új firmware ki van írva az inaktív partícióra,
+                // újraindítjuk a rendszert, erre átváltva
+                // ...
+            }
+        }
+    } else {
+        // OTA megkezdése
+        // a flash memórián megkeressük az inaktív partíciót, elkezdünk arra írni
+        // ...
+    }
+}
+\end{minted}
+%TC: endignore
+
+Miután sikeresen tudtam az MQTT brokerrel kapcsolatot létesíteni, illetve a
+perifériákkal való kapcsolat is működött, áttértem a riasztórendszer logikájának
+implementálására. A logika elvét először \aref{diag:alarm-statemachine}. ábrán rajzoltam le, majd
+egy új modulban hoztam létre az alábbi adatstruktúrákat:
+
+\begin{figure}[htbp!]
+	\ttfamily
+	{\scriptsize \includesvg[width=\columnwidth]{images/statemachine.drawio.svg} }
+	\rmfamily
+	\caption{A firmware belső állapotgép-modellje}
+	\label{diag:alarm-statemachine}
+\end{figure}
+
+%TC:ignore
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/alarm.rs\#L8}{alarm.rs}
+\begin{minted}[linenos,firstnumber=8]{rust}
+#[derive(Debug)]
+pub enum AlarmEvent {
+    MotionDetected(HAEntity),
+    MotionCleared(HAEntity),
+    AlarmStateChanged((HAEntity, AlarmState)),
+}
+
+#[derive(Clone, PartialEq, Debug)]
+pub enum AlarmState {
+    Disarmed,
+    Arming(Instant),
+    Armed(Instant),
+    Pending(Instant),
+    Triggered,
+}
+
+#[derive(Clone, PartialEq)]
+pub enum AlarmCommand {
+    Arm,
+    ArmInstantly,
+    Disarm,
+    ManualTrigger,
+    Untrigger,
+}
+\end{minted}
+%TC:endignore
+Az \mintinline{rust}/AlarmEvent/ a rendszer által kiváltott események
+közzétételéért felel. Az \mintinline{rust}/AlarmState/ az állapotgép belső
+állapotát képviseli. Végül, az \mintinline{rust}/AlarmCommand/ a felhasználó
+számára elérhető műveletek variánsai (állapotátmenetek). A control flow pedig
+nagyvonalakban így néz ki:
+
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/alarm.rs\#L42}{alarm.rs}
+\begin{minted}{rust}
+pub fn alarm_task(
+	queue: VecDeque<AlarmEvent>,
+	motion_sensors: Vec<AlarmMotionEntity>,
+	command_rx: Receiver<AlarmCommand>,
+	// ...
+) -> ! {
+	let mut alarm_state = AlarmState::Disarmed;
+
+	const ARMING_TIMEOUT: Duration = Duration::from_secs(90);
+	const PENDING_TIMEOUT: Duration = Duration::from_secs(30);
+
+	loop {
+		let mut motion_detected = false;
+		for s in motion_sensors.iter_mut() {
+			let sensor_motion = s.pin_driver.is_high();
+			if sensor_motion = s.motion {
+				continue;
+			}
+
+			s.motion = motion;
+			if motion {
+				motion_detected = true;
+				queue.push_back(AlarmEvent::MotionDetected(s.entity));
+			}
+		}
+
+		let last_state = alarm_state.clone();
+
+		match command_rx.try_recv() {
+			Ok(AlarmCommand::Arm) => {
+				if alarm_state == AlarmState::Disarmed {
+					alarm_state = AlarmState::Arming(Instant::now());
+				}
+			}
+			Ok(AlarmCommand::ArmInstantly) => {
+				if alarm_state == AlarmState::Disarmed {
+					alarm_state = AlarmState::Armed(Instant::now());
+				}
+			}
+			Ok(AlarmCommand::Disarm) => {
+				alarm_state = AlarmState::Disarmed;
+			}
+			Ok(AlarmCommand::ManualTrigger) => {
+				if let AlarmState::Armed(_) = alarm_state {
+					alarm_state == AlarmState::Triggered;
+				}
+			}
+			Ok(AlarmCommand::Untrigger) => match alarm_state {
+				AlarmState::Triggered | AlarmState::Pending(_) => {
+					alarm_state = AlarmState::Armed(Instant::now());
+				}
+				_ => {}
+			}
+			Err(e) => {
+				// ...
+			}
+		}
+
+		match alarm_state {
+			AlarmState::Disarmed = >{}
+			AlarmState::Arming(start) => {
+				if start.elapsed() >= ARMING_TIMEOUT {
+					alarm_state = AlarmState::Armed(Instant::now());
+				}
+			}
+			AlarmState::Armed(_) => {
+				if motion_detected {
+					alarm_state = AlarmState::Pending(Instant::now());
+				}
+			}
+			AlarmState::Pending(start) => {
+				if start.elapsed() >= PENDING_TIMEOUT {
+					alarm_state = AlarmState::Triggered;
+				}
+			}
+			AlarmState::Triggered => {
+				siren_pin.set_low();
+			}
+		}
+
+		if last_state != alarm_state {
+			if last_state == AlarmState::Triggered {
+				siren_pin.set_high();
+			}
+
+			queue.push_back(AlarmEvent::AlarmStateChanged((
+				alarm_entity.clone(),
+				alarm_state.clone(),
+			)));
+		}
+	}
+}
+\end{minted}
+
+A kód teszteléséhez először a parancsokat egyszerű MQTT üzenetek segítségével
+tudtam küldeni az központi egység felé. Egy hét tesztelés után úgy ítéltem meg,
+hogy az állapotgép implementációja helyesen működött. \Aref{feladat}. fejezetben
+megfogalmazott alapfeladatot ezennel a rendszer teljesíteni tudja -- kivéve
+az okosotthon integrációt. \Aref{bizt-kerd}. fejezet szerinti behatolásjelzés
+funkcióját betölti. Ebben az állapotban a rendszer még kényelmetlen volt
+a rendes használatra, hiszen semmilyen kezelőfelületet nem létesítettem. A
+következő fejezetben a Home Assistant integráció fejlesztését dokumentálom, ami
+a feladat maradék részét meg fogja oldani. Ezután fogunk tudni valós tesztelést
+végezni.
 
 \section{Integráció}
 
+\paragraph{} \Aref{hass}. fejezet írása során megismerkedtem a Home Assistant
+architektúrájával, és az MQTT integrációs lehetőségeiről. Eldöntöttem, hogy az
+autodiscovery használatával fogom megvalósítani az integrációt. Ehhez először
+a firmware-ben ki kellett alakítanom adatstruktúrákat, amik segítenek a Home
+Assistant fogalmait absztrahálni. A forráskódban ezek a segédobjektumok a
+\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/ha_types/src/lib.rs}{ha\_types/src/lib.rs}
+fájlban találhatóak.
+
+Ezeket felhasználva készítettem el az MQTT csatornán a valós kommunikáció
+logikáját. Az inicializáció a következőképpen néz ki:
+
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/scheduler.rs\#L113}{scheduler.rs}
+\begin{minted}[linenos,firstnumber=113]{rust}
+fn init_mqtt(
+    client: &mut EspMqttClient<'_, ConnState<MessageImpl, EspError>>,
+    entities: &[HAEntity],
+) -> anyhow::Result<()> {
+    const AVAILABILITY_TOPIC: &str = env!("ESP_AVAILABILITY_TOPIC");
+    const OTA_TOPIC: &str = env!("ESP_OTA_TOPIC");
+
+    // send entity config messages
+    for entity in entities.iter() {
+        let entity = HAEntity {
+            availability: Some(HADeviceAvailability {
+                payload_available: Some("online".to_string()),
+                payload_not_available: Some("offline".to_string()),
+                topic: AVAILABILITY_TOPIC.to_string(),
+                value_template: None,
+            }),
+            ..entity.clone()
+        };
+        let topic = format!(
+            "{}/{}/{}/config",
+            "homeassistant", entity.variant, entity.unique_id
+        );
+        let entity_out: HAEntityOut = entity.into();
+        let payload = serde_json::to_string(&entity_out).unwrap();
+        client.publish(&topic, QoS::AtLeastOnce, true, payload.as_bytes())?;
+
+        if let Some(command_topic) = entity_out.command_topic {
+            client.subscribe(&command_topic, QoS::ExactlyOnce)?;
+        }
+    }
+
+    // birth message
+    client.publish(AVAILABILITY_TOPIC, QoS::AtLeastOnce, true, b"online")?;
+
+    // subscribe to ota
+    client.subscribe(OTA_TOPIC, QoS::ExactlyOnce)?;
+
+    Ok(())
+}
+\end{minted}
+
+Lényegében három dolog történik:
+\begin{enumerate}
+	\item A riasztórendszer entitásainak konfigurációját egyesével legeneráljuk
+	      (alarm panel és mozgásérzékelők) a segéd struktúrákkal, majd JSON formátumba konvertáljuk,
+	      és azonnal el is küldjük a Home Assistant számára az autodiscovery protokoll szerint.
+	\item Az entitások elérhetőségét (availability) egyetlen közös topic-ra állítottuk az előző lépésben (hiszen mindegyik
+	      a központi egység elérhetőségétől függ), így egyetlen üzenetet kiküldve ``online'' állapotba hozzuk.
+	\item Feliratkozunk a firmware frissítés (OTA) topic-jára -- melynek működését láttuk az előző fejezetben.
+\end{enumerate}
+
+Ezzel még nem vagyunk kész, hiszen a szenzorok állapotát még nem küldjük és a
+riasztópanel parancsait még nem fogadjuk. Ezek implementációját így oldottam
+meg:
+
+\noindent\extlink{https://github.com/akosnad/rusty-esp-alarm/blob/1d3073a0cbd3b98af0f6a14bdf625732938f78e2/src/scheduler.rs\#L168}{scheduler.rs}
+\begin{minted}[linenos,firstnumber=168]{rust}
+fn send_alarm_state_change(
+    state: &AlarmState,
+    entity: &HAEntity,
+    client: &mut EspMqttClient<'_, ConnState<MessageImpl, EspError>>,
+) -> anyhow::Result<()> {
+    let payload = match state {
+        AlarmState::Disarmed => "disarmed",
+        AlarmState::Arming(_) => "arming",
+        AlarmState::Armed(_) => "armed_away",
+        AlarmState::Pending(_) => "pending",
+        AlarmState::Triggered => "triggered",
+    };
+    client.publish(
+        &entity.state_topic,
+        QoS::AtLeastOnce,
+        true,
+        payload.as_bytes(),
+    )?;
+    Ok(())
+}
+
+fn handle_alarm_command(
+    payload: &str,
+    alarm_command_tx: &Sender<AlarmCommand>,
+) -> anyhow::Result<()> {
+    let command = match payload {
+        "ARM_AWAY" => AlarmCommand::Arm,
+        "ARM_CUSTOM_BYPASS" => AlarmCommand::ArmInstantly,
+        "DISARM" => AlarmCommand::Disarm,
+        "TRIGGER" => AlarmCommand::ManualTrigger,
+        "UNTRIGGER" => AlarmCommand::Untrigger,
+        _ => {
+            log::warn!("Unknown command: {}", payload);
+            return Ok(());
+        }
+    };
+    alarm_command_tx.send(command)?;
+    Ok(())
+}
+\end{minted}
+
+Ezek a segédfüggvények teszik lehetővé az állapotgép és az MQTT csatornán
+küldött/fogadott adatok közötti egyértelmű átjárhatóságot. Amikor az
+állapotgép generál egy \mintinline{rust}/AlarmEvent/ eseményt, azt egy
+\mintinline{rust}/VecDeque<AlarmEvent>/ típusú objektumba helyezi, melyhez az
+MQTT kezelő szál is hozzáfér. Ez egy double-ended queue, amit egy sima FIFO-ként
+használok az események feldolgozásához. Ugyanilyen adatszerkezetet használok
+az ellenkező irányban is -- a parancsok fogadására. Mindkét queue esetén a
+konkurrens hozzáférést egy \mintinline{rust}/Mutex<>/ biztosítja.
+
+\section{Tesztelés, validálás}
+
+\paragraph{} Az implementáció készen van, az alap feladatot megvalósítottam
+(\ref{feladat}. fejezet). Ahhoz, hogy megbizonyosodjunk a rendszer szoftverének
+helyes működéséről és biztonságtechnikai helyességéről, tesztelés és validálásra
+van szükség.
+
+Miután lefrissítettem a firmware-t a kész verzióra a próbapanelen, a Home
+Assistant felületét és az eszköz log folyamát figyeltem. 5 újraindítás
+keretében gyűjtöttem statisztikákat. A reset gomb elengedése pillanatától a
+Home Assistantban megjelenő entitások online állapotára kerüléséig eltelt idő
+átlagosan 3.8 másodperc volt. A legrosszabb minta: 6.2 másodperc, a legjobb:
+3 másodperc volt. Ezt kimagasló eredménynek tartom, hiszen bekapcsolás után
+szinte azonnal használható lesz a rendszer. \Aref{kereskedelmi-megbizhatosag}.
+fejezetben láttuk a kereskedelmi rendszerek használhatóságát, kifejezetten
+a DIY rendszerek onboarding élményét. A látottak alapján a saját rendszerem
+első indításra -- miután a rendszer telepítve lett -- az Ethernet adapter
+csatlakoztatásával szinte azonnal láthatóvá válik és használható lesz a Home
+Assistant felületén. Jó kompromisszumnak tartom a megoldásomat. A hagyományos
+rendszerek telepítési igényeit ellensúlyozza a szinte erőfeszítés nélküli
+onboarding élmény. Hozzá kell tennem, hogy a mérés körülményei ideálisak voltak:
+a hálózat két eszközből állt (ESP és laptop), így a zavaró tényezők minimálisak
+voltak. Az onboarding élmény összehasonlítását más rendszerekkel a dolgozat
+keretében nem teszem, de az üzem alatti tapasztalatokat tudom mérni. Ahhoz hogy
+lássuk az eszköz valós környezetbeli teljesítményét, beszereltem az otthonomban
+hosszútávú tesztelésre. A dolgozat írásakor a rendszer nagyjából 8 hónapja
+üzemben van. Ez idő alatt jegyeztem a felmerülő hibákat, észrevételeket:
+
+\begin{itemize}
+	\item Az első 2 hónap alatt ha elég sokáig futott a firmware (nagyjából
+	      4-5 nap hossza), hirtelen nem reagált semmire. A hiba egy deadlock
+	      szituációból adódott, melyet kijavítottam. A hiba bekövetkezése és az eltelt idő látszólagos
+	      korrelációja valójában hamis volt, mert a deadlock bekövetkezéséhez
+	      két eseménynek kellett egyszerre történnie: a riasztó élesedése és
+	      egy mozgásérzékelő jelet észlelése. Javítás után a hiba nem jelentkezett többször.
+	\item Az MQTT broker leállása és újraindulása után a firmware nem csatlakozott
+	      újra, így használhatatlan maradt a manuális újraindításig. Ez azért volt,
+	      mert csak az Ethernet link megszakadását kezeltem le. Ha a link
+	      sértetlen maradt, és csak a socket szakadt meg, azt figyelmen kívül
+	      hagyta az implementáció. Javítás után teszteltem a hibatűrést.
+	      A brokert újraindítottam és az Ethernet linket leválasztottam véletlenszerű
+	      időpontokban egy héten keresztül. Minden alkalommal, akár több nap után
+	      is képes volt visszacsatlakozni az eszköz. A hibát kijavítottnak tekintettem.
+\end{itemize}
+
+Összesítve a hibák sűrűsége alacsony volt, de azok kritikusak. Itt látszik jól
+a Rust erőssége és az aranymondás róla igazolódik: \textsl{``Ha a kód lefordul,
+	akkor biztos lehetsz benne, hogy az úgy működni fog.''} \cite{rust-compiles-1}
+\cite{rust-compiles-2} \cite{rust-compiles-3} Bizonyos keretekig ez az állítás
+igaz; a fordító garantálja azt a működést amit enged, hogy leforduljon. Azt,
+hogy a programozó logikai hibát vétett, azt már nem tudja kijavítani helyette.
+
 \clearpage % Ez azért kell, hogy nehogy képek átcsússzanak a következő fejezethez
diff --git a/src/contents/melleklet.tex b/src/contents/melleklet.tex
index 1b1d08edfdd9de1a2b0e7ed4f705ae6bec854b07..405fa1320395b7b21060a59b84a099e4af693555 100644
--- a/src/contents/melleklet.tex
+++ b/src/contents/melleklet.tex
@@ -26,7 +26,7 @@ nélküli leütések számát jelenti.
 	\url{http://mirrors.ctan.org/support/texcount/doc/TeXcount.pdf}}
 
 {
-	\small
+	\footnotesize
 	\verbatiminput{./build/charcount.tex}
 }
 
diff --git a/src/hivatkozasok.bib b/src/hivatkozasok.bib
index 94efd7bd22271c60e7c7424f685fd17e496f54b2..62ce91951d9c0420199ea8f89216e8718e312779 100644
--- a/src/hivatkozasok.bib
+++ b/src/hivatkozasok.bib
@@ -656,3 +656,65 @@ urldate = {2025-04-22},
 	url = {https://www.home-assistant.io/integrations/mqtt/},
 	urldate = {2025-04-26},
 }
+
+@manual{ip101,
+	title = {Single Port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transciever -
+			IP101G Data Sheet},
+	organization = {IC Plus Corp.},
+	url = {
+			https://www.lcsc.com/datasheet/lcsc_datasheet_2008201637_IC-Plus-IP101GRI_C703537.pdf
+		},
+	urldate = {2025-04-26},
+}
+
+@online{ip101-example,
+	title = {ESP32-Ethernet-Kit V1.2 Getting Started Guide},
+	url = {
+			https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32/hw-reference/esp32/get-started-ethernet-kit.html
+		},
+	urldate = {2025-04-26},
+}
+
+@manual{w5500-datasheet,
+	title = {W5500 Datasheet - Version 1.1.0},
+	url = {https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf},
+	urldate = {2025-04-26},
+}
+
+@online{esp-idf-template,
+	title = {Rust on ESP-IDF "Hello, World" template},
+	url = {https://github.com/esp-rs/esp-idf-template},
+	urldate = {2025-04-27},
+}
+
+@online{cargo,
+	title = {The Cargo Book},
+	url = {https://doc.rust-lang.org/cargo/},
+	urldate = {2025-04-27},
+}
+
+@online{rust-compiles-1,
+	title = {Opinion: Rust code typically works once compiled, why?},
+	organization = {Rust users forum},
+	url = {
+			https://users.rust-lang.org/t/opinion-rust-code-typically-works-once-compiled-why/95126
+		},
+	urldate = {2025-04-27},
+}
+
+@online{rust-compiles-2,
+	title = {Am I safe, if Rust program has successfully compiled?},
+	organization = {Reddit},
+	url = {
+			https://www.reddit.com/r/rust/comments/113bm7a/am_i_safe_if_rust_program_has_successfully/
+		},
+	urldate = {2025-04-27},
+}
+
+@online{rust-compiles-3,
+	title = {It's true that Rust approaches the "if it compiles, it works"
+			property that Hask...},
+	organization = {Hacker News},
+	url = {https://news.ycombinator.com/item?id=8392945},
+	urldate = {2025-04-27},
+}
diff --git a/src/images/statemachine.drawio.svg b/src/images/statemachine.drawio.svg
new file mode 100644
index 0000000000000000000000000000000000000000..87975d040f9db8ed1ed161f3556e8a5f869aed9f
--- /dev/null
+++ b/src/images/statemachine.drawio.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Do not edit this file with editors other than draw.io -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="439px" height="429px" viewBox="-0.5 -0.5 439 429" content="&lt;mxfile host=&quot;app.diagrams.net&quot; agent=&quot;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0&quot; version=&quot;26.2.14&quot;&gt;&#10;  &lt;diagram name=&quot;Page-1&quot; id=&quot;S62LkRPGwtLC0qFBxN4L&quot;&gt;&#10;    &lt;mxGraphModel dx=&quot;596&quot; dy=&quot;967&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;850&quot; pageHeight=&quot;1100&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10;      &lt;root&gt;&#10;        &lt;mxCell id=&quot;0&quot; /&gt;&#10;        &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-3&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot; source=&quot;33Sh5yp_UrR39phBk1Qc-1&quot; target=&quot;33Sh5yp_UrR39phBk1Qc-2&quot;&gt;&#10;          &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot; /&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-10&quot; value=&quot;Arm&quot; style=&quot;edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;33Sh5yp_UrR39phBk1Qc-3&quot;&gt;&#10;          &lt;mxGeometry x=&quot;0.2007&quot; y=&quot;4&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;-24&quot; y=&quot;14&quot; as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-20&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;&quot; edge=&quot;1&quot; parent=&quot;1&quot; source=&quot;33Sh5yp_UrR39phBk1Qc-1&quot; target=&quot;33Sh5yp_UrR39phBk1Qc-4&quot;&gt;&#10;          &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;Array as=&quot;points&quot;&gt;&#10;              &lt;mxPoint x=&quot;330&quot; y=&quot;790&quot; /&gt;&#10;              &lt;mxPoint x=&quot;470&quot; y=&quot;790&quot; /&gt;&#10;            &lt;/Array&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-21&quot; value=&quot;ArmInstantly&quot; style=&quot;edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;33Sh5yp_UrR39phBk1Qc-20&quot;&gt;&#10;          &lt;mxGeometry x=&quot;0.1064&quot; y=&quot;2&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint y=&quot;-17&quot; as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-1&quot; value=&quot;Disarmed&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry x=&quot;300&quot; y=&quot;560&quot; width=&quot;120&quot; height=&quot;60&quot; as=&quot;geometry&quot; /&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-5&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;530&quot; y=&quot;740&quot; as=&quot;sourcePoint&quot; /&gt;&#10;            &lt;mxPoint x=&quot;530&quot; y=&quot;860&quot; as=&quot;targetPoint&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-12&quot; value=&quot;\textit{90s}&quot; style=&quot;edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;33Sh5yp_UrR39phBk1Qc-5&quot;&gt;&#10;          &lt;mxGeometry x=&quot;-0.0241&quot; y=&quot;-1&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;21&quot; y=&quot;2&quot; as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-24&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;330&quot; y=&quot;500&quot; as=&quot;sourcePoint&quot; /&gt;&#10;            &lt;mxPoint x=&quot;330&quot; y=&quot;560&quot; as=&quot;targetPoint&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-25&quot; value=&quot;Disarm&quot; style=&quot;edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;33Sh5yp_UrR39phBk1Qc-24&quot;&gt;&#10;          &lt;mxGeometry x=&quot;-0.2254&quot; y=&quot;-3&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;23&quot; y=&quot;-7&quot; as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-2&quot; value=&quot;Arming&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry x=&quot;440&quot; y=&quot;680&quot; width=&quot;120&quot; height=&quot;60&quot; as=&quot;geometry&quot; /&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-7&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot; source=&quot;33Sh5yp_UrR39phBk1Qc-4&quot; target=&quot;33Sh5yp_UrR39phBk1Qc-6&quot;&gt;&#10;          &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot; /&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-14&quot; value=&quot;\textit{motion\_detected}&quot; style=&quot;edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;33Sh5yp_UrR39phBk1Qc-7&quot;&gt;&#10;          &lt;mxGeometry x=&quot;-0.0236&quot; y=&quot;-1&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint y=&quot;-9&quot; as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-4&quot; value=&quot;Armed&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry x=&quot;440&quot; y=&quot;860&quot; width=&quot;120&quot; height=&quot;60&quot; as=&quot;geometry&quot; /&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-9&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;250&quot; y=&quot;860&quot; as=&quot;sourcePoint&quot; /&gt;&#10;            &lt;mxPoint x=&quot;250&quot; y=&quot;740&quot; as=&quot;targetPoint&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-13&quot; value=&quot;\textit{30s}&quot; style=&quot;edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;33Sh5yp_UrR39phBk1Qc-9&quot;&gt;&#10;          &lt;mxGeometry x=&quot;0.1769&quot; y=&quot;-2&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;8&quot; y=&quot;11&quot; as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-6&quot; value=&quot;Pending&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry x=&quot;160&quot; y=&quot;860&quot; width=&quot;120&quot; height=&quot;60&quot; as=&quot;geometry&quot; /&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-8&quot; value=&quot;Triggered&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry x=&quot;160&quot; y=&quot;680&quot; width=&quot;120&quot; height=&quot;60&quot; as=&quot;geometry&quot; /&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-26&quot; value=&quot;ManualTrigger&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.159;entryY=-0.036;entryDx=0;entryDy=0;entryPerimeter=0;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry y=&quot;40&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;180&quot; y=&quot;620&quot; as=&quot;sourcePoint&quot; /&gt;&#10;            &lt;mxPoint x=&quot;180.07999999999998&quot; y=&quot;680&quot; as=&quot;targetPoint&quot; /&gt;&#10;            &lt;Array as=&quot;points&quot;&gt;&#10;              &lt;mxPoint x=&quot;180&quot; y=&quot;662.16&quot; /&gt;&#10;              &lt;mxPoint x=&quot;180&quot; y=&quot;662.16&quot; /&gt;&#10;            &lt;/Array&gt;&#10;            &lt;mxPoint as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-28&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10;          &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;190&quot; y=&quot;740&quot; as=&quot;sourcePoint&quot; /&gt;&#10;            &lt;mxPoint x=&quot;190&quot; y=&quot;860&quot; as=&quot;targetPoint&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;        &lt;mxCell id=&quot;33Sh5yp_UrR39phBk1Qc-29&quot; value=&quot;Untrigger&quot; style=&quot;edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;33Sh5yp_UrR39phBk1Qc-28&quot;&gt;&#10;          &lt;mxGeometry x=&quot;0.1769&quot; y=&quot;-2&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10;            &lt;mxPoint x=&quot;-28&quot; y=&quot;-10&quot; as=&quot;offset&quot; /&gt;&#10;          &lt;/mxGeometry&gt;&#10;        &lt;/mxCell&gt;&#10;      &lt;/root&gt;&#10;    &lt;/mxGraphModel&gt;&#10;  &lt;/diagram&gt;&#10;&lt;/mxfile&gt;&#10;"><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="33Sh5yp_UrR39phBk1Qc-3"><g><path d="M 282 97 L 362 97 L 362 180.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 362 185.88 L 358.5 178.88 L 362 180.63 L 365.5 178.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-10"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 133px; margin-left: 342px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">Arm</div></div></div></foreignObject><text x="342" y="136" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">Arm</text></switch></g></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-20"><g><path d="M 192 127 L 192 297 L 332 297 L 332 360.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 332 365.88 L 328.5 358.88 L 332 360.63 L 335.5 358.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-21"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 278px; margin-left: 232px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">ArmInstantly</div></div></div></foreignObject><text x="232" y="282" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">ArmInstantly</text></switch></g></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-1"><g><rect x="162" y="67" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 97px; margin-left: 163px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Disarmed</div></div></div></foreignObject><text x="222" y="101" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">Disarmed</text></switch></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-5"><g><path d="M 392 247 L 392 360.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 392 365.88 L 388.5 358.88 L 392 360.63 L 395.5 358.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-12"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 308px; margin-left: 412px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">\textit{90s}</div></div></div></foreignObject><text x="412" y="311" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">\textit{90s}</text></switch></g></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-24"><g><path d="M 192 7 L 192 60.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 192 65.88 L 188.5 58.88 L 192 60.63 L 195.5 58.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-25"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 24px; margin-left: 212px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">Disarm</div></div></div></foreignObject><text x="212" y="27" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">Disarm</text></switch></g></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-2"><g><rect x="302" y="187" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 217px; margin-left: 303px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Arming</div></div></div></foreignObject><text x="362" y="221" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">Arming</text></switch></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-7"><g><path d="M 302 397 L 148.37 397" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 143.12 397 L 150.12 393.5 L 148.37 397 L 150.12 400.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-14"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 387px; margin-left: 224px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">\textit{motion\_detected}</div></div></div></foreignObject><text x="224" y="391" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">\textit{motion\_detected}</text></switch></g></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-4"><g><rect x="302" y="367" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 397px; margin-left: 303px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Armed</div></div></div></foreignObject><text x="362" y="401" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">Armed</text></switch></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-9"><g><path d="M 112 367 L 112 253.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 112 248.12 L 115.5 255.12 L 112 253.37 L 108.5 255.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-13"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 308px; margin-left: 122px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">\textit{30s}</div></div></div></foreignObject><text x="122" y="311" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">\textit{30s}</text></switch></g></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-6"><g><rect x="22" y="367" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 397px; margin-left: 23px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Pending</div></div></div></foreignObject><text x="82" y="401" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">Pending</text></switch></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-8"><g><rect x="22" y="187" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all" style="fill: light-dark(#ffffff, var(--ge-dark-color, #121212)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 217px; margin-left: 23px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Triggered</div></div></div></foreignObject><text x="82" y="221" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="12px" text-anchor="middle">Triggered</text></switch></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-26"><g><path d="M 42 127 L 42 169.17 L 42.05 180.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 42.07 185.88 L 38.54 178.9 L 42.05 180.63 L 45.54 178.87 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 157px; margin-left: 82px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">ManualTrigger</div></div></div></foreignObject><text x="82" y="160" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">ManualTrigger</text></switch></g></g></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-28"><g><path d="M 52 247 L 52 360.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 52 365.88 L 48.5 358.88 L 52 360.63 L 55.5 358.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g><g data-cell-id="33Sh5yp_UrR39phBk1Qc-29"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 308px; margin-left: 22px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; background-color: #ffffff; "><div style="display: inline-block; font-size: 11px; font-family: &quot;Helvetica&quot;; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; background-color: light-dark(#ffffff, var(--ge-dark-color, #121212)); white-space: nowrap; ">Untrigger</div></div></div></foreignObject><text x="22" y="312" fill="light-dark(#000000, #ffffff)" font-family="&quot;Helvetica&quot;" font-size="11px" text-anchor="middle">Untrigger</text></switch></g></g></g></g></g></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg>
\ No newline at end of file
diff --git a/src/szakdolgozat.tex b/src/szakdolgozat.tex
index 785a6cdc0ec0e0c7543f686a244dd80115310956..2802d1835227aa27b6df2b5e7f6296d6d475f0bc 100644
--- a/src/szakdolgozat.tex
+++ b/src/szakdolgozat.tex
@@ -121,7 +121,7 @@
 
 % Kódrészletek
 \usepackage{listings}
-\usepackage{sourcecodepro} % egy jó betűtípus
+% \usepackage{sourcecodepro} % egy jó betűtípus
 \lstset{captionpos=b, numberbychapter=false, basicstyle=\ttfamily, showstringspaces=false, columns=fullflexible}
 
 % Kódrészletek magyar stílusú számozása
@@ -244,6 +244,12 @@
 \usepackage{moreverb}
 \immediate\write18{texcount -char -tex -merge -sum ./\jobname.tex > ./build/charcount.tex}
 
+% syntax highlighthoz
+\usepackage{minted}
+\setminted{style=colorful,fontsize=\footnotesize,breaklines=true}
+
+%TC:envir minted [] other
+
 % ------------- Dokumentum legenerálása -----------------
 \begin{document}