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"