<?xml version="1.0" encoding="utf-8"?>
<Templates format="2" protocolVersion="83">
  <Template id="a3f7e820-1c4b-4d9e-b5f2-8e6c3d7a1094" revision="1.3">
    <RevisionHistory>
      <Revision id="1.3">Based on feedback from the author, who ran revision 1.2 with a real Nuki lock (firmware 4.10.5): added a Lock push button (lockAction 2); the lock now has a service attribute Last action with the action name; Last update is shown in the local time of the Core instead of UTC (the parsed time is stored in a variable first, because a property cannot be read from a function call). The Last action variable stays numeric for smart rules, because PacketParserMultiStateSensor exists only on Core Next.</Revision>
    </RevisionHistory>
    <Name>Nuki Smart Lock 4 Pro (MQTT)</Name>
    <CcuModel>PacketParserCCU</CcuModel>
    <Producer>Nuki Home Solutions GmbH</Producer>
    <Model>Smart Lock 4 Pro</Model>
    <Description>Integrácia zámku Nuki cez jeho natívne MQTT API (Smart Lock 3.0 Pro, 4. generácia Pro, Ultra). Zámok je MQTT klient: potrebuje broker v tej istej LAN na porte 1883 bez TLS, napríklad rozhranie MQTT Broker v TapHome — parameter IP adresa MQTT brokera je potom IP adresa Core, nie zámku. V aplikácii Nuki: Funkcie a nastavenia → Smart Home → MQTT, zadať IP brokera a zapnúť „Allow locking“. Nuki ID (hex) je v aplikácii Nuki v administrácii zariadenia.</Description>
    <Category>4</Category>
    <ImportParameters>
      <Parameter>
        <Id>NukiId</Id>
        <Name>Nuki ID (hex, napr. 2BB28570)</Name>
        <Value>2BB28570</Value>
      </Parameter>
      <Parameter>
        <Id>IpAddress</Id>
        <Name>IP adresa MQTT brokera</Name>
        <Value>192.168.1.1</Value>
      </Parameter>
    </ImportParameters>
    <Module>
      <Name>Nuki Smart Lock 4 Pro</Name>
      <Id>-1</Id>
      <Model>PacketParserModuleMqtt</Model>
      <DeviceProperties>
        <IpAddress>$[IpAddress]</IpAddress>
        <Port>1883</Port>
        <RootTopic>nuki/#</RootTopic>
        <InternalPollInterval>0</InternalPollInterval>
      </DeviceProperties>
      <Devices>
        <Device>
          <Name>Zámok</Name>
          <Id>-2</Id>
          <Model>PacketParserMultiValueSwitch</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>0</InternalPollInterval>
            <CustomVariables>[{&quot;Name&quot;:&quot;batteryCritical&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;false&quot;},{&quot;Name&quot;:&quot;batteryCharging&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;false&quot;},{&quot;Name&quot;:&quot;keypadBatteryCritical&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;false&quot;},{&quot;Name&quot;:&quot;nukiConnected&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;true&quot;},{&quot;Name&quot;:&quot;serverConnected&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;false&quot;},{&quot;Name&quot;:&quot;commandResponse&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;&quot;},{&quot;Name&quot;:&quot;nukiTimestamp&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;&quot;},{&quot;Name&quot;:&quot;nukiFirmware&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0.0,&quot;StringValue&quot;:&quot;&quot;},{&quot;Name&quot;:&quot;ready&quot;,&quot;ValueTypeId&quot;:1,&quot;NumericValue&quot;:0.0},{&quot;Name&quot;:&quot;lastState&quot;,&quot;ValueTypeId&quot;:1,&quot;NumericValue&quot;:-1.0},{&quot;Name&quot;:&quot;lastAction&quot;,&quot;ValueTypeId&quot;:1,&quot;NumericValue&quot;:-1.0}]</CustomVariables>
            <ListenerScriptPacketParser>IF(INDEXOF(RECEIVEDMSG.TOPIC, &quot;nuki/$[NukiId]/&quot;) != 0)
    RETURN(0);
END
IF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/state&quot;)
    VAR st := TODOUBLE(TOSTRING(RECEIVEDMSG.PAYLOAD));
    ready := 1;
    lastState := st;
    IF(st = 1)
        Mu := 1;
    ELSEIF(st = 2 OR st = 3 OR st = 5 OR st = 6 OR st = 7)
        Mu := 3;
    END
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/batteryCritical&quot;)
    batteryCritical := TOSTRING(RECEIVEDMSG.PAYLOAD);
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/batteryCharging&quot;)
    batteryCharging := TOSTRING(RECEIVEDMSG.PAYLOAD);
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/keypadBatteryCritical&quot;)
    keypadBatteryCritical := TOSTRING(RECEIVEDMSG.PAYLOAD);
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/connected&quot;)
    nukiConnected := TOSTRING(RECEIVEDMSG.PAYLOAD);
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/serverConnected&quot;)
    serverConnected := TOSTRING(RECEIVEDMSG.PAYLOAD);
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/commandResponse&quot;)
    commandResponse := TOSTRING(RECEIVEDMSG.PAYLOAD);
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/timestamp&quot;)
    VAR tsUtc := DATETIME(TOSTRING(RECEIVEDMSG.PAYLOAD));
    VAR tsLocal := tsUtc.LOCALDATETIME;
    nukiTimestamp := TOSTRING(tsLocal, &quot;d.M.yyyy HH:mm:ss&quot;);
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/lockActionEvent&quot;)
    lastAction := TODOUBLE(GETAT(SPLIT(TOSTRING(RECEIVEDMSG.PAYLOAD), &quot;,&quot;), 0));
ELSEIF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/firmware&quot;)
    nukiFirmware := TOSTRING(RECEIVEDMSG.PAYLOAD);
END</ListenerScriptPacketParser>
            <ReadSwitchStateScriptPacketParser>RETURN(Mu);</ReadSwitchStateScriptPacketParser>
            <WriteSwitchStateScriptPacketParser>IF(ready = 0 OR Mu = lastState)
    RETURN(0);
END
IF(Mu = 1)
    MQTTPUBLISH(&quot;nuki/$[NukiId]/lockAction&quot;, &quot;2&quot;);
ELSEIF(Mu = 3)
    MQTTPUBLISH(&quot;nuki/$[NukiId]/lockAction&quot;, &quot;1&quot;);
END</WriteSwitchStateScriptPacketParser>
            <StartupValueSource>ReadFromDevice</StartupValueSource>
            <ServiceAttributesScriptsPacketParser>[{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Stav zámku Nuki&quot;},&quot;ReadFormula&quot;:&quot;IF(lastState = 1, \&quot;Zamknuté\&quot;, IF(lastState = 2, \&quot;Odomykanie\&quot;, IF(lastState = 3, \&quot;Odomknuté\&quot;, IF(lastState = 4, \&quot;Zamykanie\&quot;, IF(lastState = 5, \&quot;Odblokované\&quot;, IF(lastState = 6, \&quot;Odomknuté (Lock &apos;n&apos; Go)\&quot;, IF(lastState = 7, \&quot;Odblokovanie\&quot;, IF(lastState = 0, \&quot;Nekalibrovaný\&quot;, IF(lastState = 254, \&quot;Motor zablokovaný\&quot;, \&quot;Neznámy (\&quot; + TOSTRING(lastState) + \&quot;)\&quot;)))))))))&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Krit. batéria zámku&quot;},&quot;ReadFormula&quot;:&quot;IF(batteryCritical = \&quot;true\&quot;, \&quot;Áno — vymeniť batérie\&quot;, \&quot;Nie\&quot;)&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Nabíjanie batérie&quot;},&quot;ReadFormula&quot;:&quot;IF(batteryCharging = \&quot;true\&quot;, \&quot;Prebieha\&quot;, \&quot;Neprebieha\&quot;)&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Krit. batéria klávesnice&quot;},&quot;ReadFormula&quot;:&quot;IF(keypadBatteryCritical = \&quot;true\&quot;, \&quot;Áno — vymeniť batérie\&quot;, \&quot;Nie\&quot;)&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Pripojenie k brokeru&quot;},&quot;ReadFormula&quot;:&quot;IF(nukiConnected = \&quot;true\&quot;, \&quot;Pripojený\&quot;, \&quot;Odpojený\&quot;)&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Pripojenie k serveru Nuki&quot;},&quot;ReadFormula&quot;:&quot;IF(serverConnected = \&quot;true\&quot;, \&quot;Pripojený\&quot;, \&quot;Odpojený\&quot;)&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Odozva na príkaz&quot;},&quot;ReadFormula&quot;:&quot;IF(commandResponse = \&quot;0\&quot;, \&quot;Úspech\&quot;, IF(LENGTH(commandResponse) = 0, \&quot;—\&quot;, \&quot;Chyba zámku: \&quot; + commandResponse))&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Posledná akcia&quot;},&quot;ReadFormula&quot;:&quot;IF(lastAction = 1, \&quot;Odomknutie\&quot;, IF(lastAction = 2, \&quot;Zamknutie\&quot;, IF(lastAction = 3, \&quot;Odblokovanie\&quot;, IF(lastAction = 4, \&quot;Lock &apos;n&apos; Go\&quot;, IF(lastAction = 5, \&quot;Lock &apos;n&apos; Go s odblokovaním\&quot;, IF(lastAction = 6, \&quot;Úplné zamknutie\&quot;, IF(lastAction = 80, \&quot;Kľúčenka (bez akcie)\&quot;, IF(lastAction = 90, \&quot;Tlačidlo (bez akcie)\&quot;, IF(lastAction = -1, \&quot;—\&quot;, \&quot;Neznáma (\&quot; + TOSTRING(lastAction) + \&quot;)\&quot;)))))))))&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Posledná aktualizácia&quot;},&quot;ReadFormula&quot;:&quot;IF(LENGTH(nukiTimestamp) = 0, \&quot;—\&quot;, nukiTimestamp)&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Verzia firmvéru&quot;},&quot;ReadFormula&quot;:&quot;IF(LENGTH(nukiFirmware) = 0, \&quot;—\&quot;, nukiFirmware)&quot;}]</ServiceAttributesScriptsPacketParser>
            <ServiceActionsScriptsPacketParser>[{&quot;ActionDefinition&quot;:{&quot;Name&quot;:&quot;Lock &apos;n&apos; Go&quot;,&quot;NumericParameters&quot;:[],&quot;BoolParameters&quot;:[],&quot;EnumParameters&quot;:[]},&quot;Script&quot;:&quot;MQTTPUBLISH(\&quot;nuki/$[NukiId]/lockAction\&quot;, \&quot;4\&quot;);&quot;}]</ServiceActionsScriptsPacketParser>
            <Value0Index>64</Value0Index>
            <Value1>1</Value1>
            <Value1Index>1</Value1Index>
            <Value1Name>Zamknuté</Value1Name>
            <Value1IconId>39</Value1IconId>
            <Value2Index>66</Value2Index>
            <Value3>3</Value3>
            <Value3Index>3</Value3Index>
            <Value3Name>Odomknuté</Value3Name>
            <Value3IconId>256</Value3IconId>
            <Value4Index>68</Value4Index>
            <Value5Index>69</Value5Index>
            <Value6Index>70</Value6Index>
            <Value7Index>71</Value7Index>
            <Value8Index>72</Value8Index>
            <Value9Index>73</Value9Index>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Dverový senzor</Name>
          <Id>-3</Id>
          <Model>PacketParserReedContact</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>0</InternalPollInterval>
            <OnStateIconId>91</OnStateIconId>
            <OffStateIconId>92</OffStateIconId>
            <OnStateName>Otvorené</OnStateName>
            <OffStateName>Zatvorené</OffStateName>
            <ListenerScriptPacketParser>IF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/doorsensorState&quot;)
    VAR dsVal := TODOUBLE(TOSTRING(RECEIVEDMSG.PAYLOAD));
    IF(dsVal = 3)
        Rc := 1;
    ELSEIF(dsVal = 2)
        Rc := 0;
    ELSEIF(dsVal = 240)
        Rc := 1;
        ADDWARNING(0, &quot;Dverový senzor: detekovaná manipulácia (tamper)&quot;);
    ELSEIF(dsVal = 1)
        ADDINFO(0, &quot;Dverový senzor deaktivovaný&quot;);
    END
END</ListenerScriptPacketParser>
            <ReadStateScriptPacketParser>RETURN(Rc);</ReadStateScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Odblokovanie (Unlatch)</Name>
          <Id>-4</Id>
          <Model>PacketParserPushButton</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>0</InternalPollInterval>
            <IconId>86</IconId>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ReadButtonPressedStateScriptPacketParser>Bp := 0;</ReadButtonPressedStateScriptPacketParser>
            <WriteButtonPressedStateScriptPacketParser>MQTTPUBLISH(&quot;nuki/$[NukiId]/lockAction&quot;, &quot;3&quot;);</WriteButtonPressedStateScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Lock &apos;n&apos; Go</Name>
          <Id>-5</Id>
          <Model>PacketParserPushButton</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>0</InternalPollInterval>
            <IconId>86</IconId>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ReadButtonPressedStateScriptPacketParser>Bp := 0;</ReadButtonPressedStateScriptPacketParser>
            <WriteButtonPressedStateScriptPacketParser>MQTTPUBLISH(&quot;nuki/$[NukiId]/lockAction&quot;, &quot;4&quot;);</WriteButtonPressedStateScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Úroveň batérie</Name>
          <Id>-6</Id>
          <Model>PacketParserAnalogInput</Model>
          <DeviceProperties>
            <DeviceType>11001</DeviceType>
            <InternalPollInterval>0</InternalPollInterval>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ListenerScriptPacketParser>IF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/batteryChargeState&quot;)
    An := TODOUBLE(TOSTRING(RECEIVEDMSG.PAYLOAD)) / 100;
END</ListenerScriptPacketParser>
            <ReadInputLevelScriptPacketParser>RETURN(An);</ReadInputLevelScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Posledná akcia zámku</Name>
          <Id>-7</Id>
          <Model>PacketParserVariable</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>0</InternalPollInterval>
            <IsReadOnly>True</IsReadOnly>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ListenerScriptPacketParser>IF(RECEIVEDMSG.TOPIC = &quot;nuki/$[NukiId]/lockActionEvent&quot;)
    Va := TODOUBLE(GETAT(SPLIT(TOSTRING(RECEIVEDMSG.PAYLOAD), &quot;,&quot;), 0));
END</ListenerScriptPacketParser>
            <ReadStateScriptPacketParser>RETURN(Va);</ReadStateScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Zamknúť</Name>
          <Id>-8</Id>
          <Model>PacketParserPushButton</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>0</InternalPollInterval>
            <IconId>86</IconId>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <ReadButtonPressedStateScriptPacketParser>Bp := 0;</ReadButtonPressedStateScriptPacketParser>
            <WriteButtonPressedStateScriptPacketParser>MQTTPUBLISH(&quot;nuki/$[NukiId]/lockAction&quot;, &quot;2&quot;);</WriteButtonPressedStateScriptPacketParser>
          </DeviceProperties>
        </Device>
      </Devices>
    </Module>
  </Template>
</Templates>
