diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro
index be6b9c8..32c3258 100644
--- a/libnymea-zigbee/libnymea-zigbee.pro
+++ b/libnymea-zigbee/libnymea-zigbee.pro
@@ -16,6 +16,7 @@ SOURCES += \
backends/nxp/interface/zigbeeinterfacenxpreply.cpp \
backends/nxp/zigbeebridgecontrollernxp.cpp \
backends/nxp/zigbeenetworknxp.cpp \
+ zcl/closures/zigbeeclusterdoorlock.cpp \
zcl/general/zigbeeclusteridentify.cpp \
zcl/general/zigbeeclusterlevelcontrol.cpp \
zcl/general/zigbeeclusteronoff.cpp \
@@ -68,6 +69,7 @@ HEADERS += \
backends/nxp/interface/zigbeeinterfacenxpreply.h \
backends/nxp/zigbeebridgecontrollernxp.h \
backends/nxp/zigbeenetworknxp.h \
+ zcl/closures/zigbeeclusterdoorlock.h \
zcl/general/zigbeeclusteridentify.h \
zcl/general/zigbeeclusterlevelcontrol.h \
zcl/general/zigbeeclusteronoff.h \
diff --git a/libnymea-zigbee/zcl/closures/zigbeeclusterdoorlock.cpp b/libnymea-zigbee/zcl/closures/zigbeeclusterdoorlock.cpp
new file mode 100644
index 0000000..6d3170a
--- /dev/null
+++ b/libnymea-zigbee/zcl/closures/zigbeeclusterdoorlock.cpp
@@ -0,0 +1,144 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* This project including source code and documentation is protected by copyright law, and
+* remains the property of nymea GmbH. All rights, including reproduction, publication,
+* editing and translation, are reserved. The use of this project is subject to the terms of a
+* license agreement to be concluded with nymea GmbH in accordance with the terms
+* of use of nymea GmbH, available under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; version 3.
+* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+* See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along with this project.
+* If not, see .
+*
+* For any further details and any questions please contact us under contact@nymea.io
+* or see our FAQ/Licensing Information on https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "zigbeeclusterdoorlock.h"
+#include "zigbeenetworkreply.h"
+#include "loggingcategory.h"
+#include "zigbeenetwork.h"
+#include "zigbeeutils.h"
+
+#include
+
+ZigbeeClusterDoorLock::ZigbeeClusterDoorLock(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
+ ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdDoorLock, direction, parent)
+{
+
+}
+
+ZigbeeClusterReply *ZigbeeClusterDoorLock::lockDoor(const QByteArray code)
+{
+ QByteArray payload;
+ if (!code.isEmpty()) {
+ QDataStream stream(&payload, QIODevice::WriteOnly);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ stream << static_cast(Zigbee::OctetString);
+ for (int i = 0; i < code.length(); i++) {
+ stream << static_cast(code.at(i));
+ }
+ }
+
+ return executeClusterCommand(Command::CommandLockDoor, payload);
+}
+
+ZigbeeClusterReply *ZigbeeClusterDoorLock::unlockDoor(const QByteArray code)
+{
+ QByteArray payload;
+ if (!code.isEmpty()) {
+ QDataStream stream(&payload, QIODevice::WriteOnly);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ stream << static_cast(Zigbee::OctetString);
+ for (int i = 0; i < code.length(); i++) {
+ stream << static_cast(code.at(i));
+ }
+ }
+
+ return executeClusterCommand(Command::CommandUnlockDoor, payload);
+}
+
+ZigbeeClusterReply *ZigbeeClusterDoorLock::toggle(const QByteArray code)
+{
+ QByteArray payload;
+ if (!code.isEmpty()) {
+ QDataStream stream(&payload, QIODevice::WriteOnly);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ stream << static_cast(Zigbee::OctetString);
+ for (int i = 0; i < code.length(); i++) {
+ stream << static_cast(code.at(i));
+ }
+ }
+
+ return executeClusterCommand(Command::CommandToggle, payload);
+}
+
+ZigbeeClusterReply *ZigbeeClusterDoorLock::unlockDoorWithTimeout(quint16 timeoutSeconds, const QByteArray code)
+{
+ QByteArray payload;
+ QDataStream stream(&payload, QIODevice::WriteOnly);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ stream << timeoutSeconds;
+ if (!code.isEmpty()) {
+ stream << static_cast(Zigbee::OctetString);
+ for (int i = 0; i < code.length(); i++) {
+ stream << static_cast(code.at(i));
+ }
+ }
+
+ return executeClusterCommand(Command::CommandUnlockWithTimeout, payload);
+}
+
+ZigbeeClusterDoorLock::LockState ZigbeeClusterDoorLock::lockState() const
+{
+ return m_lockState;
+}
+
+ZigbeeClusterDoorLock::DoorState ZigbeeClusterDoorLock::doorState() const
+{
+ return m_doorState;
+}
+
+void ZigbeeClusterDoorLock::setAttribute(const ZigbeeClusterAttribute &attribute)
+{
+ qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast(attribute.id()) << attribute.dataType();
+ if (hasAttribute(attribute.id())) {
+ m_attributes[attribute.id()] = attribute;
+ emit attributeChanged(attribute);
+ } else {
+ m_attributes.insert(attribute.id(), attribute);
+ emit attributeChanged(attribute);
+ }
+
+ switch (attribute.id()) {
+ case AttributeLockState:
+ m_lockState = static_cast(attribute.dataType().toUInt8());
+ emit lockStateChanged(m_lockState);
+ break;
+ case AttributeDoorState:
+ m_doorState = static_cast(attribute.dataType().toUInt8());
+ emit doorStateChanged(m_doorState);
+ break;
+ }
+}
+
+void ZigbeeClusterDoorLock::processDataIndication(ZigbeeClusterLibrary::Frame frame)
+{
+ //qCDebug(dcZigbeeCluster()) << "Processing cluster frame" << m_node << m_endpoint << this << frame;
+
+ // Increase the tsn for continuouse id increasing on both sides
+ m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
+
+ qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
+}
diff --git a/libnymea-zigbee/zcl/closures/zigbeeclusterdoorlock.h b/libnymea-zigbee/zcl/closures/zigbeeclusterdoorlock.h
new file mode 100644
index 0000000..2efb6ca
--- /dev/null
+++ b/libnymea-zigbee/zcl/closures/zigbeeclusterdoorlock.h
@@ -0,0 +1,237 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* This project including source code and documentation is protected by copyright law, and
+* remains the property of nymea GmbH. All rights, including reproduction, publication,
+* editing and translation, are reserved. The use of this project is subject to the terms of a
+* license agreement to be concluded with nymea GmbH in accordance with the terms
+* of use of nymea GmbH, available under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; version 3.
+* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+* See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along with this project.
+* If not, see .
+*
+* For any further details and any questions please contact us under contact@nymea.io
+* or see our FAQ/Licensing Information on https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEECLUSTERDOORLOCK_H
+#define ZIGBEECLUSTERDOORLOCK_H
+
+#include
+#include "zcl/zigbeecluster.h"
+#include "zcl/zigbeeclusterreply.h"
+
+class ZigbeeNode;
+class ZigbeeNetwork;
+class ZigbeeNodeEndpoint;
+class ZigbeeNetworkReply;
+
+class ZigbeeClusterDoorLock : public ZigbeeCluster
+{
+ Q_OBJECT
+
+ friend class ZigbeeNode;
+ friend class ZigbeeNetwork;
+
+public:
+ enum Attribute {
+ // Basic information
+ AttributeLockState = 0x0000, // M
+ AttributeLockType = 0x0001, // M
+ AttributeActuatorEnabled = 0x0002, // M
+ AttributeDoorState = 0x0003,
+ AttributeDoorOpenEvents = 0x0004,
+ AttributeDoorClosedEvents = 0x0005,
+ AttributeOpenPeriod = 0x0006,
+
+ // User, Pin, Schedule, Log information
+ AttributeNumberOfLogRecordsSupported = 0x0010,
+ AttributeNumberOfTotalUsersSupported = 0x0011,
+ AttributeNumberOfPinUsersSupported = 0x0012,
+ AttributeNumberOfRfidUsersSupported = 0x0013,
+ AttributeNumberOfWeekDaySchedulesSupportedPerUser = 0x0014,
+ AttributeNumberOfYearDaySchedulesSupportedPerUser = 0x0015,
+ AttributeNumberOfHolidaySchedulesSupported = 0x0016,
+ AttributeMaxPinCodeLength = 0x0017,
+ AttributeMinPinCodeLenght = 0x0018,
+ AttributeMaxRfidCodeLength = 0x0019,
+ AttributeMinRfidCodeLength = 0x001a,
+
+ // Operational settings
+ AttributeEnableLogging = 0x0020,
+ AttributeLanguage = 0x0021,
+ AttributeLedSettings = 0x0022,
+ AttributeAutoRelockTime = 0x0023,
+ AttributeSoundVolume = 0x0024,
+ AttributeOperatingMode = 0x0025,
+ AttributeSupportedOperatingModes = 0x0026,
+ AttributeDefaultConfigurationRegister = 0x0027,
+ AttributeEnableLocalProgramming = 0x0028,
+ AttributeEnableOneTouchLocking = 0x0029,
+ AttributeEnableInsideStatusLed = 0x002a,
+ AttributeEnablePrivacyModeButton = 0x002b,
+
+ // Security settings
+ AttributeWrongCodeEntryLimit = 0x0030,
+ AttributeUserCodeTemporaryDisableTime = 0x0031,
+ AttributeSendPinOverTheAir = 0x0032,
+ AttributeRequirePinForRfOperation = 0x0033,
+ AttributeZigbeeSecurityLevel = 0x0034,
+
+ // Alarm and event masks
+ AttributeAlarmMask = 0x0040,
+ AttributeKeypadOperationEventMask = 0x0041,
+ AttributeRfOperationEventMask = 0x0042,
+ AttributeManualOperationEventMask = 0x0043,
+ AttributeRfidOperationEventMask = 0x0044,
+ AttributeKeypadProgrammingEventMask = 0x0045,
+ AttributeRfProgrammingEventMask = 0x0046,
+ AttributeRfidProgrammingEventMask = 0x0047
+ };
+ Q_ENUM(Attribute)
+
+ enum Command {
+ CommandLockDoor = 0x00, // M
+ CommandUnlockDoor = 0x01, // M
+ CommandToggle = 0x02,
+ CommandUnlockWithTimeout = 0x03,
+ CommandGetLogRecord = 0x04,
+ CommandSetPinCode = 0x05,
+ CommandGetPinCode = 0x06,
+ CommandClearPinCode = 0x07,
+ CommandClearAllPinCodes = 0x08,
+ CommandSetUserStatus = 0x09,
+ CommandGetUserStatus = 0x0a,
+ CommandSetWeekdaySchedule = 0x0b,
+ CommandGetWeekdaySchedule = 0x0c,
+ CommandClearWeekdaySchedule = 0x0d,
+ CommandSetYearDaySchedule = 0x0e,
+ CommandGetYearDaySchedule = 0x0f,
+ CommandClearYearDaySchedule = 0x10,
+ CommandSetHolidaySchedule = 0x11,
+ CommandGetHolidaySchedule = 0x12,
+ CommandClearHolidaySchedule = 0x13,
+ CommandSetUserType = 0x14,
+ CommandGetUserType = 0x15,
+ CommandSetRfidCode = 0x16,
+ CommandGetRfidCode = 0x17,
+ CommandClearRfidCode = 0x18,
+ CommandClearAllRfidCodes = 0x19
+ };
+ Q_ENUM(Command)
+
+ // AttributeLockState (0x0000)
+ enum LockState {
+ LockStateNotFullyLocked = 0x00,
+ LockStateLocked = 0x01,
+ LockStateUnlocked = 0x02,
+ LockStateUndefined = 0xff
+ };
+ Q_ENUM(LockState)
+
+ // AttributeLockType (0x0001)
+ enum LockType {
+ LockTypeDeadBolt = 0x00,
+ LockTypeMagnetic = 0x01,
+ LockTypeOther = 0x02,
+ LockTypeMortise = 0x03,
+ LockTypeRim = 0x04,
+ LockTypeLatchBolt = 0x05,
+ LockTypeCylindricalLock = 0x06,
+ LockTypeTubularLock = 0x07,
+ LockTypeInterconnectedLock = 0x08,
+ LockTypeDeadLatch = 0x09,
+ LockTypeDoorFurniture = 0x0a
+ };
+ Q_ENUM(LockType)
+
+ // AttributeDoorState (0x0003)
+ enum DoorState {
+ DoorStateOpen = 0x00,
+ DoorStateColsed = 0x01,
+ DoorStateErrorJammed = 0x02,
+ DoorStateErrorForcedOpen = 0x03,
+ DoorStateErrorUnspecified = 0x04,
+ DoorStateUndefined = 0xff
+ };
+ Q_ENUM(DoorState)
+
+ // AttributeOperatingMode (0x0025)
+ enum OperationMode {
+ OperationModeNormal = 0x00,
+ OperationModeVacation = 0x01,
+ OperationModePrivacy = 0x02,
+ OperationModeNoRfLockUnlock = 0x03,
+ OperationModePassage = 0x04
+ };
+ Q_ENUM(OperationMode)
+
+ // AttributeSupportedOperatingModes (0x0026)
+ enum SupportedOperationMode {
+ SupportedOperationModeNormal = 0x0000,
+ SupportedOperationModeVacation = 0x0001,
+ SupportedOperationModePrivacy = 0x0002,
+ SupportedOperationModeNoRfLockUnlock = 0x0004,
+ SupportedOperationModePassage = 0x0008
+ };
+ Q_ENUM(SupportedOperationMode)
+ Q_DECLARE_FLAGS(SupportedOperationModes, SupportedOperationMode)
+
+ // AttributeLedSettings (0x0022)
+ enum LedSettings {
+ LedSettingsNeverUserLedSignalisation = 0x00,
+ LedSettingsUseLedSignalisationExceptAccessAllowedEvents = 0x01,
+ LedSettingsUseLedSignalisationAllEvents = 0x02
+ };
+ Q_ENUM(LedSettings)
+
+ // AttributeSoundVolume (0x0024)
+ enum SoundVolume {
+ SoundVolumeSilentMode = 0x00,
+ SoundVolumeLowVolume = 0x01,
+ SoundVolumeHighVolume = 0x02
+ };
+ Q_ENUM(SoundVolume)
+
+ explicit ZigbeeClusterDoorLock(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
+
+ // Note: with HA 1.2, this commands have optional a RFID code or PIN code
+ ZigbeeClusterReply *lockDoor(const QByteArray code = QByteArray());
+ ZigbeeClusterReply *unlockDoor(const QByteArray code = QByteArray());
+ ZigbeeClusterReply *toggle(const QByteArray code = QByteArray());
+ ZigbeeClusterReply *unlockDoorWithTimeout(quint16 timeoutSeconds, const QByteArray code = QByteArray());
+
+ // TODO: rest of the commands
+
+ LockState lockState() const;
+ DoorState doorState() const;
+
+private:
+ LockState m_lockState = LockStateUndefined;
+ DoorState m_doorState = DoorStateUndefined;
+
+ void setAttribute(const ZigbeeClusterAttribute &attribute) override;
+
+protected:
+ void processDataIndication(ZigbeeClusterLibrary::Frame frame) override;
+
+signals:
+ void lockStateChanged(LockState lockState);
+ void doorStateChanged(DoorState doorState);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ZigbeeClusterDoorLock::SupportedOperationModes)
+
+
+#endif // ZIGBEECLUSTERDOORLOCK_H
diff --git a/libnymea-zigbee/zcl/ota/zigbeeclusterota.cpp b/libnymea-zigbee/zcl/ota/zigbeeclusterota.cpp
index 11f6eab..c171c00 100644
--- a/libnymea-zigbee/zcl/ota/zigbeeclusterota.cpp
+++ b/libnymea-zigbee/zcl/ota/zigbeeclusterota.cpp
@@ -99,6 +99,8 @@ void ZigbeeClusterOta::processDataIndication(ZigbeeClusterLibrary::Frame frame)
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast(StatuCodeNoImageAvailable);
+ // Note: if there would be an image available, the response would be success, followed by manufacturer code, image type, file version of image and file size
+
ZigbeeClusterReply *reply = sendClusterServerResponse(CommandQueryNextImageResponse, frame.header.transactionSequenceNumber, payload);
connect(reply, &ZigbeeClusterReply::finished, this, [](){
qCDebug(dcZigbeeCluster()) << "OTA image request response for image query sent successfully to requested node.";
diff --git a/libnymea-zigbee/zigbeenodeendpoint.cpp b/libnymea-zigbee/zigbeenodeendpoint.cpp
index 967102d..7c56bc1 100644
--- a/libnymea-zigbee/zigbeenodeendpoint.cpp
+++ b/libnymea-zigbee/zigbeenodeendpoint.cpp
@@ -30,8 +30,6 @@
#include "zigbeenode.h"
#include "loggingcategory.h"
-#include "zcl/general/zigbeeclusterbasic.h"
-
quint8 ZigbeeNodeEndpoint::endpointId() const
{
return m_endpointId;
@@ -197,6 +195,11 @@ ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(ZigbeeClusterLibrary::ClusterId
return new ZigbeeClusterOccupancySensing(m_network, m_node, this, direction, this);
break;
+ // Colsures
+ case ZigbeeClusterLibrary::ClusterIdDoorLock:
+ return new ZigbeeClusterDoorLock(m_network, m_node, this, direction, this);
+ break;
+
// Lighting
case ZigbeeClusterLibrary::ClusterIdColorControl:
return new ZigbeeClusterColorControl(m_network, m_node, this, direction, this);
diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h
index bfdeb74..b7e6510 100644
--- a/libnymea-zigbee/zigbeenodeendpoint.h
+++ b/libnymea-zigbee/zigbeenodeendpoint.h
@@ -43,6 +43,8 @@
#include "zcl/general/zigbeeclusterlevelcontrol.h"
#include "zcl/general/zigbeeclusterpowerconfiguration.h"
+#include "zcl/closures/zigbeeclusterdoorlock.h"
+
#include "zcl/measurement/zigbeeclusteroccupancysensing.h"
#include "zcl/measurement/zigbeeclusterilluminancemeasurment.h"
#include "zcl/measurement/zigbeeclustertemperaturemeasurement.h"