Skip to content

Commit

Permalink
Merge pull request #147 from SzczepanLeon/text_fields
Browse files Browse the repository at this point in the history
Add support for text fields
  • Loading branch information
SzczepanLeon authored Nov 1, 2024
2 parents 44f2be7 + 62dccf4 commit 084f3e7
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 36 deletions.
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ sensor:
- platform: wmbus
meter_id: 0x22113366
type: vario411
text_sensor:
- platform: wmbus
meter_id: 0xABCD1122
type: izar
sensors:
- name: "Izar current_alarms"
field: "current_alarms"
```


Expand Down Expand Up @@ -250,7 +258,7 @@ In wmbus platform:
> **_NOTE:_** MQTT can be defined in wmbus component or in [ESPHome level](https://esphome.io/components/mqtt.html).


Meter/sensor:
sensor:

- **meter_id** (*Optional*, int): Meter ID. Can be specified as decimal or hex.
- **type** (*Optional*, string): Meter type. When not defined, driver will be detected from telegram.
Expand All @@ -260,7 +268,19 @@ Meter/sensor:
- **name** (*Optional*, string): The name for the sensor. At least one of **id** and **name** must be specified.
- **field** (*Optional*): Field from decoded telegram (without unit). If **field** is not present then **name** is used.
- **unit_of_measurement** (**Required**): Unit for field defined above.
- All other options from [Sensor](https://esphome.io/components/sensor/index.html#config-sensor).
- All other options from [Sensor](https://esphome.io/components/sensor/).


text_sensor:

- **meter_id** (*Optional*, int): Meter ID. Can be specified as decimal or hex.
- **type** (*Optional*, string): Meter type. When not defined, driver will be detected from telegram.
- **key** (*Optional*): Key for meter, used in payload decoding process. Defaults to ``""``.
- **sensors** (*Optional*):
- **id** (*Optional*, string): Manually specify the ID for code generation. At least one of **id** and **name** must be specified.
- **name** (*Optional*, string): The name for the sensor. At least one of **id** and **name** must be specified.
- **field** (*Optional*): Text field from decoded telegram. If **field** is not present then **name** is used.
- All other options from [Text Sensor](https://esphome.io/components/text_sensor/).

------------------------

Expand Down
2 changes: 1 addition & 1 deletion components/wmbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
CODEOWNERS = ["@SzczepanLeon"]

DEPENDENCIES = ["time"]
AUTO_LOAD = ["sensor"]
AUTO_LOAD = ["sensor", "text_sensor"]

wmbus_ns = cg.esphome_ns.namespace('wmbus')
WMBusComponent = wmbus_ns.class_('WMBusComponent', cg.Component)
Expand Down
5 changes: 5 additions & 0 deletions components/wmbus/meters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,11 @@ bool MeterCommonImplementation::hasStringValue(FieldInfo* fi)
return string_values_.count(fi->vname()) != 0;
}

bool MeterCommonImplementation::hasStringValue(string name)
{
return string_values_.count(name) != 0;
}

string MeterCommonImplementation::getMyStringValue(string name)
{
for (auto& p : string_values_)
Expand Down
2 changes: 2 additions & 0 deletions components/wmbus/meters.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ struct Meter
virtual std::string getStringValue(FieldInfo* fi) = 0;
virtual std::string decodeTPLStatusByte(uchar sts) = 0;

virtual bool hasStringValue(string name) = 0;

virtual void onUpdate(std::function<void(Telegram* t, Meter*)> cb) = 0;
virtual int numUpdates() = 0;

Expand Down
1 change: 1 addition & 0 deletions components/wmbus/meters_common_implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ struct MeterCommonImplementation : public virtual Meter
bool hasValue(FieldInfo* fi);
bool hasNumericValue(FieldInfo* fi);
bool hasStringValue(FieldInfo* fi);
bool hasStringValue(string name);

std::string decodeTPLStatusByte(uchar sts);

Expand Down
31 changes: 3 additions & 28 deletions components/wmbus/sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,6 @@
CONF_TYPE,
CONF_KEY,
CONF_NAME,
UNIT_DECIBEL_MILLIWATT,
DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT,
UNIT_CUBIC_METER,
DEVICE_CLASS_WATER,
STATE_CLASS_TOTAL_INCREASING,
UNIT_KILOWATT_HOURS,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
STATE_CLASS_TOTAL,
UNIT_KILOWATT,
UNIT_EMPTY,
DEVICE_CLASS_EMPTY,
UNIT_CELSIUS,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_GAS,
UNIT_VOLT,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
UNIT_SECOND,
DEVICE_CLASS_TIMESTAMP,
CONF_UNIT_OF_MEASUREMENT,
)

Expand Down Expand Up @@ -87,11 +66,9 @@ def my_key(value):
async def to_code(config):
if config[CONF_TYPE]:
cg.add_platformio_option("build_src_filter", [f"+<**/wmbus/driver_{config[CONF_TYPE].lower()}.cpp>"])
var = cg.new_Pvariable(config[CONF_LISTENER_ID],
config[CONF_METER_ID],
config[CONF_TYPE].lower(),
config[CONF_KEY])
if config[CONF_METER_ID]:
wmbus = await cg.get_variable(config[CONF_WMBUS_ID])
cg.add(wmbus.register_wmbus_listener(config[CONF_METER_ID], config[CONF_TYPE].lower(), config[CONF_KEY]))
for s in config.get(CONF_SENSORS, []):
if CONF_UNIT_OF_MEASUREMENT not in s:
print(color(Fore.RED, f"unit_of_measurement not defined for sensor '{s[CONF_NAME]}'!"))
Expand All @@ -102,6 +79,4 @@ async def to_code(config):
field = s[CONF_NAME].lower()
unit = s[CONF_UNIT_OF_MEASUREMENT]
sens = await sensor.new_sensor(s)
cg.add(var.add_sensor(field, unit, sens))
wmbus = await cg.get_variable(config[CONF_WMBUS_ID])
cg.add(wmbus.register_wmbus_listener(var))
cg.add(wmbus.add_sensor(config[CONF_METER_ID], field, unit, sens))
77 changes: 77 additions & 0 deletions components/wmbus/text_sensor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.log import Fore, color
from esphome.const import (
CONF_ID,
CONF_TYPE,
CONF_KEY,
CONF_NAME,
)

AUTO_LOAD = ["wmbus"]

CONF_METER_ID = "meter_id"
CONF_LISTENER_ID = "listener_id"
CONF_WMBUS_ID = "wmbus_id"
CONF_FIELD = "field"
CONF_SENSORS = 'sensors'

from .. import (
WMBusComponent,
wmbus_ns
)

CODEOWNERS = ["@SzczepanLeon"]

WMBusListener = wmbus_ns.class_('WMBusListener')

def my_key(value):
value = cv.string_strict(value)
parts = [value[i : i + 2] for i in range(0, len(value), 2)]
if (len(parts) != 16) and (len(parts) != 0):
raise cv.Invalid("Key must consist of 16 hexadecimal numbers")
parts_int = []
if any(len(part) != 2 for part in parts):
raise cv.Invalid("Key must be format XX")
for part in parts:
try:
parts_int.append(int(part, 16))
except ValueError:
# pylint: disable=raise-missing-from
raise cv.Invalid("Key must be hex values from 00 to FF")
return "".join(f"{part:02X}" for part in parts_int)


SENSOR_SCHEMA = text_sensor.text_sensor_schema(
#
).extend(
{
cv.Optional(CONF_FIELD, default=""): cv.string_strict,
}
)

CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_LISTENER_ID): cv.declare_id(WMBusListener),
cv.GenerateID(CONF_WMBUS_ID): cv.use_id(WMBusComponent),
cv.Optional(CONF_METER_ID, default=0): cv.hex_int,
cv.Optional(CONF_TYPE, default=""): cv.string_strict,
cv.Optional(CONF_KEY, default=""): my_key,
cv.Optional(CONF_SENSORS): cv.ensure_list(SENSOR_SCHEMA),
}
).extend(cv.COMPONENT_SCHEMA)

async def to_code(config):
if config[CONF_TYPE]:
cg.add_platformio_option("build_src_filter", [f"+<**/wmbus/driver_{config[CONF_TYPE].lower()}.cpp>"])
if config[CONF_METER_ID]:
wmbus = await cg.get_variable(config[CONF_WMBUS_ID])
cg.add(wmbus.register_wmbus_listener(config[CONF_METER_ID], config[CONF_TYPE].lower(), config[CONF_KEY]))
for s in config.get(CONF_SENSORS, []):
if (s[CONF_FIELD]):
field = s[CONF_FIELD].lower()
else:
field = s[CONF_NAME].lower()
sens = await text_sensor.new_text_sensor(s)
cg.add(wmbus.add_sensor(config[CONF_METER_ID], field, sens))
2 changes: 1 addition & 1 deletion components/wmbus/version.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#ifndef MY_VERSION
#define MY_VERSION "4.0.11"
#define MY_VERSION "4.1.0"
#define WMBUSMETERS_VERSION "1.17.1-b8f4a945"
#endif
22 changes: 19 additions & 3 deletions components/wmbus/wmbus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ namespace wmbus {
field.second->publish_state(mbus_data.rssi);
}
else if (field.second->get_unit_of_measurement().empty()) {
ESP_LOGW(TAG, "Fields without unit not supported yet!");
ESP_LOGW(TAG, "Fields without unit not supported as sensor, please switch to text_sensor.");
}
else {
Unit field_unit = toUnit(field.second->get_unit_of_measurement());
Expand All @@ -159,6 +159,15 @@ namespace wmbus {
}
}
}
for (auto const& field : sensor->text_fields) {
if (meter->hasStringValue(field.first)) {
std::string value = meter->getMyStringValue(field.first);
field.second->publish_state(value);
}
else {
ESP_LOGW(TAG, "Can't get requested field '%s'", field.first.c_str());
}
}
#ifdef USE_WMBUS_MQTT
std::string json;
meter->printJsonMeter(&t, &json, false);
Expand Down Expand Up @@ -191,8 +200,11 @@ namespace wmbus {
}
}

void WMBusComponent::register_wmbus_listener(WMBusListener *listener) {
this->wmbus_listeners_[listener->id] = listener;
void WMBusComponent::register_wmbus_listener(const uint32_t meter_id, const std::string type, const std::string key) {
if (this->wmbus_listeners_.count(meter_id) == 0) {
WMBusListener *listener = new wmbus::WMBusListener(meter_id, type, key);
this->wmbus_listeners_.insert({meter_id, listener});
}
}

void WMBusComponent::led_blink() {
Expand Down Expand Up @@ -381,6 +393,10 @@ namespace wmbus {
ESP_LOGCONFIG(TAG, " Field: '%s'", ele.first.first.c_str());
LOG_SENSOR(" ", "Name:", ele.second);
}
for (const auto &ele : this->text_fields) {
ESP_LOGCONFIG(TAG, " Text field: '%s'", ele.first.c_str());
LOG_TEXT_SENSOR(" ", "Name:", ele.second);
}
}

WMBusListener::WMBusListener(const uint32_t id, const std::string type, const std::string key) {
Expand Down
17 changes: 16 additions & 1 deletion components/wmbus/wmbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "esphome/core/component.h"
#include "esphome/components/network/ip_address.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/text_sensor/text_sensor.h"
#ifdef USE_WMBUS_MQTT
#include <PubSubClient.h>
#elif defined(USE_MQTT)
Expand Down Expand Up @@ -72,6 +73,10 @@ namespace wmbus {
void add_sensor(std::string field, std::string unit, sensor::Sensor *sensor) {
this->fields[std::pair<std::string, std::string>(field, unit)] = sensor;
};
std::map<std::string, text_sensor::TextSensor *> text_fields{};
void add_sensor(std::string field, text_sensor::TextSensor *sensor) {
this->text_fields[field] = sensor;
};

void dump_config();
int char_to_int(char input);
Expand Down Expand Up @@ -102,7 +107,7 @@ namespace wmbus {
float get_setup_priority() const override { return setup_priority::LATE; }
void set_led_pin(GPIOPin *led) { this->led_pin_ = led; }
void set_led_blink_time(uint32_t led_blink_time) { this->led_blink_time_ = led_blink_time; }
void register_wmbus_listener(WMBusListener *listener);
void register_wmbus_listener(const uint32_t meter_id, const std::string type, const std::string key);
void add_cc1101(InternalGPIOPin *mosi, InternalGPIOPin *miso,
InternalGPIOPin *clk, InternalGPIOPin *cs,
InternalGPIOPin *gdo0, InternalGPIOPin *gdo2,
Expand All @@ -116,6 +121,16 @@ namespace wmbus {
this->frequency_ = frequency;
this->sync_mode_ = sync_mode;
}
void add_sensor(uint32_t meter_id, std::string field, std::string unit, sensor::Sensor *sensor) {
if (this->wmbus_listeners_.count(meter_id) != 0) {
this->wmbus_listeners_[meter_id]->add_sensor(field, unit, sensor);
}
}
void add_sensor(uint32_t meter_id, std::string field, text_sensor::TextSensor *sensor) {
if (this->wmbus_listeners_.count(meter_id) != 0) {
this->wmbus_listeners_[meter_id]->add_sensor(field, sensor);
}
}
#ifdef USE_ETHERNET
void set_eth(ethernet::EthernetComponent *eth_component) { this->net_component_ = eth_component; }
#elif defined(USE_WIFI)
Expand Down

0 comments on commit 084f3e7

Please sign in to comment.