kopia lustrzana https://github.com/weetmuts/wmbusmeters
rodzic
04be63eb12
commit
cd4820a357
11
CHANGES
11
CHANGES
|
@ -1,3 +1,14 @@
|
||||||
|
|
||||||
|
Version 0.9.14: 2019-09-16
|
||||||
|
|
||||||
|
Added negative match rule for ids. You can now write:
|
||||||
|
id=78*,!7812345*,!78222222
|
||||||
|
which will match any meter whose id begins with 78
|
||||||
|
but not match any meter whose id begins with 7812345,
|
||||||
|
nor the meter with the exact id 78222222.
|
||||||
|
|
||||||
|
The order of the match rules does not matter.
|
||||||
|
|
||||||
Version 0.9.13: 2019-08-14
|
Version 0.9.13: 2019-08-14
|
||||||
|
|
||||||
Fix bug that prevented rtl_wmbus to run inside daemon.
|
Fix bug that prevented rtl_wmbus to run inside daemon.
|
||||||
|
|
|
@ -56,7 +56,9 @@ The latest reading of the meter can also be found here: /var/log/wmbusmeters/met
|
||||||
|
|
||||||
You can use several ids using `id=1111111,2222222,3333333` or you can listen to all
|
You can use several ids using `id=1111111,2222222,3333333` or you can listen to all
|
||||||
meters of a certain type `id=*` or you can suffix with star `id=8765*` to match
|
meters of a certain type `id=*` or you can suffix with star `id=8765*` to match
|
||||||
all meters with a given prefix.
|
all meters with a given prefix. If you supply at least one positive match rule, then you
|
||||||
|
can add negative match rules as well. For example `id=*,!2222*`
|
||||||
|
which will match all meter ids, except those that begin with 2222.
|
||||||
|
|
||||||
If you are running on a Raspberry PI with flash storage and you relay the data to
|
If you are running on a Raspberry PI with flash storage and you relay the data to
|
||||||
another computer using a shell command (mosquitto_pub or curl or similar) then you might want to remove
|
another computer using a shell command (mosquitto_pub or curl or similar) then you might want to remove
|
||||||
|
|
|
@ -338,7 +338,7 @@ unique_ptr<Configuration> parseCommandLine(int argc, char **argv) {
|
||||||
mt = toMeterType(type);
|
mt = toMeterType(type);
|
||||||
|
|
||||||
if (mt == MeterType::UNKNOWN) error("Not a valid meter type \"%s\"\n", type.c_str());
|
if (mt == MeterType::UNKNOWN) error("Not a valid meter type \"%s\"\n", type.c_str());
|
||||||
if (!isValidId(id)) error("Not a valid meter id \"%s\"\n", id.c_str());
|
if (!isValidMatchExpressions(id, false)) error("Not a valid id nor a valid meter match expression \"%s\"\n", id.c_str());
|
||||||
if (!isValidKey(key)) error("Not a valid meter key \"%s\"\n", key.c_str());
|
if (!isValidKey(key)) error("Not a valid meter key \"%s\"\n", key.c_str());
|
||||||
vector<string> no_meter_shells;
|
vector<string> no_meter_shells;
|
||||||
c->meters.push_back(MeterInfo(name, type, id, key, modes, no_meter_shells));
|
c->meters.push_back(MeterInfo(name, type, id, key, modes, no_meter_shells));
|
||||||
|
|
|
@ -116,8 +116,8 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
||||||
warning("Not a valid meter type \"%s\"\n", type.c_str());
|
warning("Not a valid meter type \"%s\"\n", type.c_str());
|
||||||
use = false;
|
use = false;
|
||||||
}
|
}
|
||||||
if (!isValidId(id)) {
|
if (!isValidMatchExpression(id, false)) {
|
||||||
warning("Not a valid meter id \"%s\"\n", id.c_str());
|
warning("Not a valid meter id nor a valid meter match expression \"%s\"\n", id.c_str());
|
||||||
use = false;
|
use = false;
|
||||||
}
|
}
|
||||||
if (!isValidKey(key)) {
|
if (!isValidKey(key)) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, MeterInfo &mi,
|
||||||
type_(type), name_(mi.name), bus_(bus)
|
type_(type), name_(mi.name), bus_(bus)
|
||||||
{
|
{
|
||||||
use_aes_ = true;
|
use_aes_ = true;
|
||||||
ids_ = splitIds(mi.id);
|
ids_ = splitMatchExpressions(mi.id);
|
||||||
if (mi.key.length() == 0) {
|
if (mi.key.length() == 0) {
|
||||||
use_aes_ = false;
|
use_aes_ = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,26 +169,7 @@ bool MeterCommonImplementation::isTelegramForMe(Telegram *t)
|
||||||
{
|
{
|
||||||
debug("(meter) %s: for me? %s\n", name_.c_str(), t->id.c_str());
|
debug("(meter) %s: for me? %s\n", name_.c_str(), t->id.c_str());
|
||||||
|
|
||||||
bool id_match = false;
|
bool id_match = doesIdMatchExpressions(t->id, ids_);
|
||||||
for (auto id : ids_)
|
|
||||||
{
|
|
||||||
if (id == t->id) {
|
|
||||||
id_match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (id == "*") {
|
|
||||||
id_match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (id.length() > 1 && id.back() == '*')
|
|
||||||
{
|
|
||||||
if (t->id.length() >= id.length())
|
|
||||||
{
|
|
||||||
string prefix = t->id.substr(0, id.length()-1);
|
|
||||||
id_match = !strncmp(prefix.c_str(), id.c_str(), id.length()-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!id_match) {
|
if (!id_match) {
|
||||||
// The id must match.
|
// The id must match.
|
||||||
|
|
|
@ -31,6 +31,7 @@ using namespace std;
|
||||||
int test_crc();
|
int test_crc();
|
||||||
int test_dvparser();
|
int test_dvparser();
|
||||||
int test_linkmodes();
|
int test_linkmodes();
|
||||||
|
void test_ids();
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
@ -41,6 +42,7 @@ int main(int argc, char **argv)
|
||||||
test_crc();
|
test_crc();
|
||||||
test_dvparser();
|
test_dvparser();
|
||||||
test_linkmodes();
|
test_linkmodes();
|
||||||
|
test_ids();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,3 +319,68 @@ int test_linkmodes()
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_valid_match_expression(string s, bool expected)
|
||||||
|
{
|
||||||
|
bool b = isValidMatchExpressions(s, false);
|
||||||
|
if (b == expected) return;
|
||||||
|
if (expected == true)
|
||||||
|
{
|
||||||
|
printf("ERROR! Expected \"%s\" to be valid! But it was not!\n", s.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("ERROR! Expected \"%s\" to be invalid! But it was reported as valid!\n", s.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_does_id_match_expression(string id, string mes, bool expected)
|
||||||
|
{
|
||||||
|
vector<string> expressions = splitMatchExpressions(mes);
|
||||||
|
bool b = doesIdMatchExpressions(id, expressions);
|
||||||
|
if (b == expected) return;
|
||||||
|
if (expected == true)
|
||||||
|
{
|
||||||
|
printf("ERROR! Expected \"%s\" to match \"%s\" ! But it did not!\n", id.c_str(), mes.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("ERROR! Expected \"%s\" to NOT match \"%s\" ! But it did!\n", id.c_str(), mes.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ids()
|
||||||
|
{
|
||||||
|
test_valid_match_expression("12345678", true);
|
||||||
|
test_valid_match_expression("*", true);
|
||||||
|
test_valid_match_expression("!12345678", true);
|
||||||
|
test_valid_match_expression("12345*", true);
|
||||||
|
test_valid_match_expression("!123456*", true);
|
||||||
|
|
||||||
|
test_valid_match_expression("1234567", false);
|
||||||
|
test_valid_match_expression("", false);
|
||||||
|
test_valid_match_expression("z1234567", false);
|
||||||
|
test_valid_match_expression("123456789", false);
|
||||||
|
test_valid_match_expression("!!12345678", false);
|
||||||
|
test_valid_match_expression("12345678*", false);
|
||||||
|
test_valid_match_expression("**", false);
|
||||||
|
test_valid_match_expression("123**", false);
|
||||||
|
|
||||||
|
test_valid_match_expression("2222*,!22224444", true);
|
||||||
|
|
||||||
|
test_does_id_match_expression("12345678", "12345678", true);
|
||||||
|
test_does_id_match_expression("12345678", "*", true);
|
||||||
|
test_does_id_match_expression("12345678", "2*", false);
|
||||||
|
|
||||||
|
test_does_id_match_expression("12345678", "*,!2*", true);
|
||||||
|
|
||||||
|
test_does_id_match_expression("22222222", "22*,!22222222", false);
|
||||||
|
test_does_id_match_expression("22222223", "22*,!22222222", true);
|
||||||
|
test_does_id_match_expression("22222223", "*,!22*", false);
|
||||||
|
test_does_id_match_expression("12333333", "123*,!1234*,!1235*,!1236*", true);
|
||||||
|
test_does_id_match_expression("12366666", "123*,!1234*,!1235*,!1236*", false);
|
||||||
|
test_does_id_match_expression("11223344", "22*,33*,44*,55*", false);
|
||||||
|
test_does_id_match_expression("55223344", "22*,33*,44*,55*", true);
|
||||||
|
|
||||||
|
test_does_id_match_expression("78563413", "78563412,78563413", true);
|
||||||
|
}
|
||||||
|
|
163
src/util.cc
163
src/util.cc
|
@ -371,22 +371,165 @@ void error(const char* fmt, ...)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValidId(string& ids)
|
bool isValidMatchExpression(string me, bool non_compliant)
|
||||||
{
|
{
|
||||||
vector<string> v = splitIds(ids);
|
// Examples of valid match expressions:
|
||||||
|
// 12345678
|
||||||
|
// *
|
||||||
|
// 123*
|
||||||
|
// !12345677
|
||||||
|
// 2222222*
|
||||||
|
// !22222222
|
||||||
|
|
||||||
for (auto id : v)
|
// A match expression cannot be empty.
|
||||||
|
if (me.length() == 0) return false;
|
||||||
|
|
||||||
|
// An me can be negated with an exclamation mark first.
|
||||||
|
if (me.front() == '!') me.erase(0, 1);
|
||||||
|
|
||||||
|
// A match expression cannot be only a negation mark.
|
||||||
|
if (me.length() == 0) return false;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
if (non_compliant)
|
||||||
{
|
{
|
||||||
if (id == "*") return true;
|
// Some non-compliant meters have full hex in the me....
|
||||||
if (id.back() == '*') return true;
|
while (me.length() > 0 &&
|
||||||
if (id.length() != 8) return false;
|
((me.front() >= '0' && me.front() <= '9') ||
|
||||||
for (int i=0; i<8; ++i) {
|
(me.front() >= 'a' && me.front() <= 'f')))
|
||||||
if (id[i]<'0' || id[i]>'9') return false;
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// But compliant meters use only a bcd subset.
|
||||||
|
while (me.length() > 0 &&
|
||||||
|
(me.front() >= '0' && me.front() <= '9'))
|
||||||
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_used = false;
|
||||||
|
// An expression can end with a *
|
||||||
|
if (me.length() > 0 && me.front() == '*')
|
||||||
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
wildcard_used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we should have eaten the whole expression.
|
||||||
|
if (me.length() > 0) return false;
|
||||||
|
|
||||||
|
// Check the length of the matching bcd/hex
|
||||||
|
// If no wildcard is used, then the match expression must be exactly 8 digits.
|
||||||
|
if (!wildcard_used) return count == 8;
|
||||||
|
|
||||||
|
// If wildcard is used, then the match expressions must be 7 or less digits,
|
||||||
|
// even zero is allowed which means a single *, which matches any bcd/hex id.
|
||||||
|
return count <= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidMatchExpressions(string mes, bool non_compliant)
|
||||||
|
{
|
||||||
|
vector<string> v = splitMatchExpressions(mes);
|
||||||
|
|
||||||
|
for (string me : v)
|
||||||
|
{
|
||||||
|
if (!isValidMatchExpression(me, non_compliant)) return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool doesIdMatchExpression(string id, string match)
|
||||||
|
{
|
||||||
|
if (id.length() == 0) return false;
|
||||||
|
|
||||||
|
// Here we assume that the match expression has been
|
||||||
|
// verified to be valid.
|
||||||
|
bool can_match = true;
|
||||||
|
|
||||||
|
// Now match bcd/hex until end of id, or '*' in match.
|
||||||
|
while (id.length() > 0 && match.length() > 0 && match.front() != '*')
|
||||||
|
{
|
||||||
|
if (id.front() != match.front())
|
||||||
|
{
|
||||||
|
// We hit a difference, it cannot match.
|
||||||
|
can_match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
id.erase(0,1);
|
||||||
|
match.erase(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_used = false;
|
||||||
|
if (match.front() == '*')
|
||||||
|
{
|
||||||
|
wildcard_used = true;
|
||||||
|
match.erase(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_match)
|
||||||
|
{
|
||||||
|
// Ok, now the match expression should be empty.
|
||||||
|
// If wildcard is true, then the id can still have digits,
|
||||||
|
// otherwise it must also be empty.
|
||||||
|
if (wildcard_used)
|
||||||
|
{
|
||||||
|
can_match = match.length() == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
can_match = match.length() == 0 && id.length() == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return can_match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesIdMatchExpressions(string& id, vector<string>& mes)
|
||||||
|
{
|
||||||
|
bool found_match = false;
|
||||||
|
bool found_negative_match = false;
|
||||||
|
|
||||||
|
// Goes through all possible match expressions.
|
||||||
|
// If no expression matches, neither positive nor negative,
|
||||||
|
// then the result is false. (ie no match)
|
||||||
|
|
||||||
|
// If more than one positive match is found, and no negative,
|
||||||
|
// then the result is true.
|
||||||
|
|
||||||
|
// If more than one negative match is found, irrespective
|
||||||
|
// if there is any positive matches or not, then the result is false.
|
||||||
|
|
||||||
|
for (string me : mes)
|
||||||
|
{
|
||||||
|
bool is_negative_rule = (me.length() > 0 && me.front() == '!');
|
||||||
|
if (is_negative_rule)
|
||||||
|
{
|
||||||
|
me.erase(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m = doesIdMatchExpression(id, me);
|
||||||
|
|
||||||
|
if (is_negative_rule)
|
||||||
|
{
|
||||||
|
if (m) found_negative_match = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m) found_match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_negative_match) return false;
|
||||||
|
if (found_match) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool isValidKey(string& key)
|
bool isValidKey(string& key)
|
||||||
{
|
{
|
||||||
if (key.length() == 0) return true;
|
if (key.length() == 0) return true;
|
||||||
|
@ -406,11 +549,11 @@ bool isFrequency(std::string& fq)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> splitIds(string& ids)
|
vector<string> splitMatchExpressions(string& mes)
|
||||||
{
|
{
|
||||||
vector<string> r;
|
vector<string> r;
|
||||||
bool eof, err;
|
bool eof, err;
|
||||||
vector<uchar> v (ids.begin(), ids.end());
|
vector<uchar> v (mes.begin(), mes.end());
|
||||||
auto i = v.begin();
|
auto i = v.begin();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
|
@ -70,11 +70,15 @@ bool isLogTelegramsEnabled();
|
||||||
void debugPayload(std::string intro, std::vector<uchar> &payload);
|
void debugPayload(std::string intro, std::vector<uchar> &payload);
|
||||||
void logTelegram(std::string intro, std::vector<uchar> &header, std::vector<uchar> &content);
|
void logTelegram(std::string intro, std::vector<uchar> &header, std::vector<uchar> &content);
|
||||||
|
|
||||||
bool isValidId(std::string& id);
|
bool isValidMatchExpression(std::string id, bool non_compliant);
|
||||||
|
bool isValidMatchExpressions(std::string ids, bool non_compliant);
|
||||||
|
bool doesIdMatchExpression(std::string id, std::string match);
|
||||||
|
bool doesIdMatchExpressions(std::string& id, std::vector<std::string>& ids);
|
||||||
|
|
||||||
bool isValidKey(std::string& key);
|
bool isValidKey(std::string& key);
|
||||||
bool isFrequency(std::string& fq);
|
bool isFrequency(std::string& fq);
|
||||||
|
|
||||||
std::vector<std::string> splitIds(std::string& id);
|
std::vector<std::string> splitMatchExpressions(std::string& mes);
|
||||||
|
|
||||||
void incrementIV(uchar *iv, size_t len);
|
void incrementIV(uchar *iv, size_t len);
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,12 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat $SIM | grep '^{' | grep 8856 > $TEST/test_expected.txt
|
cat $SIM | grep '^{' | grep 8856 > $TEST/test_expected.txt
|
||||||
|
|
||||||
$PROG --format=json $SIM \
|
$PROG --format=json $SIM \
|
||||||
Element qcaloric '8856*' '' \
|
Element qcaloric '8856*' '' \
|
||||||
> $TEST/test_output.txt
|
> $TEST/test_output.txt
|
||||||
|
|
||||||
|
|
||||||
if [ "$?" == "0" ]
|
if [ "$?" == "0" ]
|
||||||
then
|
then
|
||||||
cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||||
|
|
Ładowanie…
Reference in New Issue