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
|
||||
|
||||
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
|
||||
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
|
||||
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);
|
||||
|
||||
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());
|
||||
vector<string> 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());
|
||||
use = false;
|
||||
}
|
||||
if (!isValidId(id)) {
|
||||
warning("Not a valid meter id \"%s\"\n", id.c_str());
|
||||
if (!isValidMatchExpression(id, false)) {
|
||||
warning("Not a valid meter id nor a valid meter match expression \"%s\"\n", id.c_str());
|
||||
use = false;
|
||||
}
|
||||
if (!isValidKey(key)) {
|
||||
|
|
|
@ -29,7 +29,7 @@ MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, MeterInfo &mi,
|
|||
type_(type), name_(mi.name), bus_(bus)
|
||||
{
|
||||
use_aes_ = true;
|
||||
ids_ = splitIds(mi.id);
|
||||
ids_ = splitMatchExpressions(mi.id);
|
||||
if (mi.key.length() == 0) {
|
||||
use_aes_ = false;
|
||||
} else {
|
||||
|
@ -169,26 +169,7 @@ bool MeterCommonImplementation::isTelegramForMe(Telegram *t)
|
|||
{
|
||||
debug("(meter) %s: for me? %s\n", name_.c_str(), t->id.c_str());
|
||||
|
||||
bool id_match = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool id_match = doesIdMatchExpressions(t->id, ids_);
|
||||
|
||||
if (!id_match) {
|
||||
// The id must match.
|
||||
|
|
|
@ -31,6 +31,7 @@ using namespace std;
|
|||
int test_crc();
|
||||
int test_dvparser();
|
||||
int test_linkmodes();
|
||||
void test_ids();
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
@ -41,6 +42,7 @@ int main(int argc, char **argv)
|
|||
test_crc();
|
||||
test_dvparser();
|
||||
test_linkmodes();
|
||||
test_ids();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -317,3 +319,68 @@ int test_linkmodes()
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
if (id.back() == '*') return true;
|
||||
if (id.length() != 8) return false;
|
||||
for (int i=0; i<8; ++i) {
|
||||
if (id[i]<'0' || id[i]>'9') return false;
|
||||
// Some non-compliant meters have full hex in the me....
|
||||
while (me.length() > 0 &&
|
||||
((me.front() >= '0' && me.front() <= '9') ||
|
||||
(me.front() >= 'a' && me.front() <= 'f')))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (key.length() == 0) return true;
|
||||
|
@ -406,11 +549,11 @@ bool isFrequency(std::string& fq)
|
|||
return true;
|
||||
}
|
||||
|
||||
vector<string> splitIds(string& ids)
|
||||
vector<string> splitMatchExpressions(string& mes)
|
||||
{
|
||||
vector<string> r;
|
||||
bool eof, err;
|
||||
vector<uchar> v (ids.begin(), ids.end());
|
||||
vector<uchar> v (mes.begin(), mes.end());
|
||||
auto i = v.begin();
|
||||
|
||||
for (;;) {
|
||||
|
|
|
@ -70,11 +70,15 @@ bool isLogTelegramsEnabled();
|
|||
void debugPayload(std::string intro, std::vector<uchar> &payload);
|
||||
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 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);
|
||||
|
||||
|
|
|
@ -26,10 +26,12 @@ else
|
|||
fi
|
||||
|
||||
cat $SIM | grep '^{' | grep 8856 > $TEST/test_expected.txt
|
||||
|
||||
$PROG --format=json $SIM \
|
||||
Element qcaloric '8856*' '' \
|
||||
> $TEST/test_output.txt
|
||||
|
||||
|
||||
if [ "$?" == "0" ]
|
||||
then
|
||||
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