Skip to content

Commit 24ec596

Browse files
committed
Refactor button map data types
* Separate from Sensor class; * Add button map related struct types in seperate files; * Use std::vector instead QMap; * Use lightweight index based references to button maps in Sensor; * Don't copy "large" button maps around; * Parse "buttons" meta information for introspection; This is preparation for separate REST API introspection PR. Fix shadow iterator variable in loadButtonMapJson() Correct intendation in BM_ButtonMapRefForHash
1 parent c12a79d commit 24ec596

11 files changed

+305
-90
lines changed

button_maps.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2021 dresden elektronik ingenieurtechnik gmbh.
3+
* All rights reserved.
4+
*
5+
* The software in this package is published under the terms of the BSD
6+
* style license a copy of which has been included with this distribution in
7+
* the LICENSE.txt file.
8+
*
9+
*/
10+
11+
#include "button_maps.h"
12+
13+
ButtonMapRef BM_ButtonMapRefForHash(uint32_t buttonMapNameHash, const std::vector<ButtonMap> &buttonMaps)
14+
{
15+
const auto bm = std::find_if(buttonMaps.cbegin(), buttonMaps.cend(),
16+
[buttonMapNameHash](const auto &bm) { return bm.buttonMapRef.hash == buttonMapNameHash; });
17+
if (bm != buttonMaps.cend())
18+
{
19+
return bm->buttonMapRef;
20+
}
21+
22+
return {};
23+
}
24+
25+
const ButtonMap *BM_ButtonMapForRef(ButtonMapRef ref, const std::vector<ButtonMap> &buttonMaps)
26+
{
27+
if (isValid(ref) && ref.index < buttonMaps.size())
28+
{
29+
const ButtonMap &bm = buttonMaps[ref.index];
30+
if (bm.buttonMapRef.hash == ref.hash)
31+
{
32+
return &bm;
33+
}
34+
}
35+
36+
return nullptr;
37+
}
38+
39+
const ButtonMap *BM_ButtonMapForProduct(ProductIdHash productHash, const std::vector<ButtonMap> &buttonMaps,
40+
const std::vector<ButtonProduct> &buttonProductMap)
41+
{
42+
ButtonMapRef buttonMapHash{};
43+
{
44+
const auto mapping = std::find_if(buttonProductMap.cbegin(), buttonProductMap.cend(),
45+
[productHash](const auto &i) { return i.productHash == productHash; });
46+
47+
if (mapping != buttonProductMap.cend())
48+
{
49+
buttonMapHash = mapping->buttonMapRef;
50+
}
51+
}
52+
53+
if (isValid(buttonMapHash))
54+
{
55+
return BM_ButtonMapForRef(buttonMapHash, buttonMaps);
56+
}
57+
58+
return nullptr;
59+
}

button_maps.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2021 dresden elektronik ingenieurtechnik gmbh.
3+
* All rights reserved.
4+
*
5+
* The software in this package is published under the terms of the BSD
6+
* style license a copy of which has been included with this distribution in
7+
* the LICENSE.txt file.
8+
*
9+
*/
10+
11+
#ifndef BUTTON_MAPS_H
12+
#define BUTTON_MAPS_H
13+
14+
#include <QString>
15+
#include <vector>
16+
17+
using ProductIdHash = std::size_t;
18+
19+
/*! Reference to a button map for quick lookup and leightweight embedding. */
20+
struct ButtonMapRef
21+
{
22+
uint32_t hash = 0; //! hash of button map name
23+
uint32_t index = UINT32_MAX; //! index into ButtonMap vector
24+
};
25+
26+
/*! Meta data about buttons, not used by the plugin but exposed via REST API. */
27+
struct ButtonMeta
28+
{
29+
struct Button
30+
{
31+
QString name;
32+
int button;
33+
};
34+
35+
std::vector<ButtonMeta::Button> buttons;
36+
ButtonMapRef buttonMapRef{};
37+
};
38+
39+
/*! Mapping between a product and a buttom map.
40+
*/
41+
struct ButtonProduct
42+
{
43+
ButtonMapRef buttonMapRef{};
44+
ProductIdHash productHash = 0; //! hash of modelid or manufacturer name generated by \c productHash(Resource*)
45+
};
46+
47+
struct ButtonMap
48+
{
49+
struct Item
50+
{
51+
quint8 mode;
52+
quint8 endpoint;
53+
quint16 clusterId;
54+
quint8 zclCommandId;
55+
quint16 zclParam0;
56+
int button;
57+
QString name;
58+
};
59+
std::vector<ButtonMap::Item> buttons;
60+
ButtonMapRef buttonMapRef{};
61+
};
62+
63+
ButtonMapRef BM_ButtonMapRefForHash(uint32_t buttonMapNameHash, const std::vector<ButtonMap> &buttonMaps);
64+
const ButtonMap *BM_ButtonMapForRef(ButtonMapRef ref, const std::vector<ButtonMap> &buttonMaps);
65+
const ButtonMap *BM_ButtonMapForProduct(ProductIdHash productHash, const std::vector<ButtonMap> &buttonMaps, const std::vector<ButtonProduct> &buttonProductMap);
66+
67+
inline bool isValid(ButtonMapRef a) { return a.hash != 0 && a.index != UINT32_MAX; }
68+
inline bool operator==(ButtonMapRef a, ButtonMapRef b) { return a.hash == b.hash && a.index == b.index; }
69+
inline bool operator!=(ButtonMapRef a, ButtonMapRef b) { return !(a == b); }
70+
71+
#endif // BUTTON_MAPS_H

de_web.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ DEFINES += GW_DEFAULT_NAME=\\\"Phoscon-GW\\\"
9999

100100
HEADERS = bindings.h \
101101
backup.h \
102+
button_maps.h \
102103
connectivity.h \
103104
colorspace.h \
104105
daylight.h \
@@ -132,6 +133,7 @@ SOURCES = air_quality.cpp \
132133
authorisation.cpp \
133134
backup.cpp \
134135
bindings.cpp \
136+
button_maps.cpp \
135137
change_channel.cpp \
136138
connectivity.cpp \
137139
colorspace.cpp \

de_web_plugin.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -868,14 +868,15 @@ DeRestPluginPrivate::DeRestPluginPrivate(QObject *parent) :
868868
}
869869

870870
QStringList requiredJsonObjects = {"buttons", "buttonActions", "clusters", "commands", "maps"};
871-
QJsonDocument buttonMaps = readButtonMapJson(path);
871+
QJsonDocument buttonMapsDoc = readButtonMapJson(path);
872872

873-
if (checkRootLevelObjectsJson(buttonMaps, requiredJsonObjects))
873+
if (checkRootLevelObjectsJson(buttonMapsDoc, requiredJsonObjects))
874874
{
875-
btnMapClusters = loadButtonMapClustersJson(buttonMaps);
876-
btnMapClusterCommands = loadButtonMapCommadsJson(buttonMaps);
877-
buttonMapForModelId = loadButtonMapModelIdsJson(buttonMaps);
878-
buttonMapData = loadButtonMapsJson(buttonMaps, btnMapClusters, btnMapClusterCommands);
875+
btnMapClusters = loadButtonMapClustersJson(buttonMapsDoc);
876+
btnMapClusterCommands = loadButtonMapCommadsJson(buttonMapsDoc);
877+
buttonMaps = loadButtonMapsJson(buttonMapsDoc, btnMapClusters, btnMapClusterCommands);
878+
buttonMeta = loadButtonMetaJson(buttonMapsDoc, buttonMaps);
879+
buttonProductMap = loadButtonMapModelIdsJson(buttonMapsDoc, buttonMaps);
879880
break; // only load once
880881
}
881882
}
@@ -4014,7 +4015,22 @@ void DeRestPluginPrivate::checkSensorButtonEvent(Sensor *sensor, const deCONZ::A
40144015

40154016
bool checkReporting = false;
40164017
bool checkClientCluster = false;
4017-
const std::vector<Sensor::ButtonMap> buttonMapVec = sensor->buttonMap(buttonMapData, buttonMapForModelId);
4018+
4019+
const ButtonMap *buttonMap = nullptr;
4020+
4021+
if (!isValid(sensor->buttonMapRef())) // TODO sensor.hasButtonMap()
4022+
{
4023+
buttonMap = BM_ButtonMapForProduct(productHash(sensor), buttonMaps, buttonProductMap);
4024+
if (buttonMap)
4025+
{
4026+
sensor->setButtonMapRef(buttonMap->buttonMapRef);
4027+
}
4028+
}
4029+
else
4030+
{
4031+
buttonMap = BM_ButtonMapForRef(sensor->buttonMapRef(), buttonMaps);
4032+
}
4033+
40184034
QString cluster = "0x" + QString("%1").arg(ind.clusterId(), 4, 16, QLatin1Char('0')).toUpper();
40194035
QString cmd = "0x" + QString("%1").arg(zclFrame.commandId(), 2, 16, QLatin1Char('0')).toUpper();
40204036
QString addressMode;
@@ -4034,7 +4050,7 @@ void DeRestPluginPrivate::checkSensorButtonEvent(Sensor *sensor, const deCONZ::A
40344050
if (!temp.empty() && !temp.key(zclFrame.commandId()).isEmpty()) { cmd = temp.key(zclFrame.commandId()) + " (" + cmd + ")"; }
40354051
}
40364052

4037-
if (buttonMapVec.empty())
4053+
if (!buttonMap || buttonMap->buttons.empty())
40384054
{
40394055
DBG_Printf(DBG_INFO, "[INFO] - No button map for: %s%s, endpoint: 0x%02X, cluster: %s, command: %s, payload: %s, zclSeq: %u\n",
40404056
qPrintable(sensor->modelId()), qPrintable(addressMode), ind.srcEndpoint(), qPrintable(cluster), qPrintable(cmd), qPrintable(zclPayload), zclFrame.sequenceNumber());
@@ -4435,7 +4451,7 @@ void DeRestPluginPrivate::checkSensorButtonEvent(Sensor *sensor, const deCONZ::A
44354451
}
44364452

44374453
bool ok = false;
4438-
for (const auto &buttonMap : buttonMapVec)
4454+
for (const auto &buttonMap : buttonMap->buttons)
44394455
{
44404456
if (buttonMap.mode != Sensor::ModeNone && !ok)
44414457
{

de_web_plugin_private.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,10 +1684,11 @@ public Q_SLOTS:
16841684
QString emptyString;
16851685

16861686
// JSON support
1687-
QMap<QString, std::vector<Sensor::ButtonMap>> buttonMapData;
1687+
std::vector<ButtonMeta> buttonMeta;
1688+
std::vector<ButtonMap> buttonMaps;
16881689
QMap<QString, quint16> btnMapClusters;
16891690
QMap<QString, QMap<QString, quint16>> btnMapClusterCommands;
1690-
QMap<QString, QString> buttonMapForModelId;
1691+
std::vector<ButtonProduct> buttonProductMap;
16911692

16921693
// gateways
16931694
std::vector<Gateway*> gateways;

product_match.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,21 @@ bool isLidlDevice(const QString &zigbeeModelIdentifier, const QString &manufactu
243243
}
244244
return false;
245245
}
246+
247+
uint productHash(const Resource *r)
248+
{
249+
if (!r || !r->item(RAttrManufacturerName) || !r->item(RAttrModelId))
250+
{
251+
return 0;
252+
}
253+
254+
if (isTuyaManufacturerName(r->item(RAttrManufacturerName)->toString()))
255+
{
256+
// for Tuya devices use manufacturer name as modelid
257+
return qHash(r->item(RAttrManufacturerName)->toString());
258+
}
259+
else
260+
{
261+
return qHash(r->item(RAttrModelId)->toString());
262+
}
263+
}

product_match.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ const QString R_GetProductId(Resource *resource);
2626
bool isTuyaManufacturerName(const QString &manufacturer);
2727
bool isLidlDevice(const QString &zigbeeModelIdentifier, const QString &manufacturername);
2828
const lidlDevice *getLidlDevice(const QString &zigbeeManufacturerName);
29+
uint productHash(const Resource *r);
2930

3031
#endif // PRODUCT_MATCH_H

0 commit comments

Comments
 (0)