import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components.mqtt import ( MQTT_PUBLISH_ACTION_SCHEMA, MQTTPublishAction, mqtt_publish_action_to_code, ) from esphome.components.wmbus_common.driver_loader import DriverManager from esphome.components.wmbus_radio import RadioComponent from esphome.const import ( CONF_ID, CONF_KEY, CONF_MODE, CONF_PAYLOAD, CONF_TRIGGER_ID, CONF_TYPE, ) CONF_METER_ID = "meter_id" CONF_RADIO_ID = "radio_id" CONF_FIELDS = "fields" CONF_ON_TELEGRAM = "on_telegram" CODEOWNERS = ["@kubasaw"] DEPENDENCIES = ["wmbus_radio"] AUTO_LOAD = ["sensor", "text_sensor"] MULTI_CONF = True wmbus_meter_ns = cg.esphome_ns.namespace("wmbus_meter") Meter = wmbus_meter_ns.class_("Meter", cg.Component) MeterPtr = Meter.operator("ptr") TelegramTrigger = wmbus_meter_ns.class_( "TelegramTrigger", automation.Trigger.template(MeterPtr), ) def hex_key_validator(key): try: key = cv.bind_key(key) return key except cv.Invalid as e: raise cv.Invalid(e.msg.replace("Bind key", "Key")) def meter_id_validator(meter_id): meter_id = cv.string(meter_id).lower().removeprefix("0x") meter_id = cv.Length(1, 8)(meter_id) try: value = int(meter_id, 16) except ValueError: raise cv.Invalid("Meter ID must be a valid hexadecimal string") if value < 0: raise cv.Invalid("Meter ID must be a positive hexadecimal integer") return f"{value:08x}" CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(Meter), cv.GenerateID(CONF_RADIO_ID): cv.use_id(RadioComponent), cv.Required(CONF_METER_ID): meter_id_validator, cv.Required(CONF_TYPE): cv.All( cv.one_of(*DriverManager.available_drivers), DriverManager.request_driver, ), cv.Optional(CONF_KEY): cv.Any( cv.All(cv.string_strict, lambda s: s.encode().hex(), hex_key_validator), hex_key_validator, ), cv.Optional(CONF_ON_TELEGRAM): automation.validate_automation( {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TelegramTrigger)}, ), cv.Optional(CONF_MODE): cv.one_of("c1", "t1", lower=True), }, ).extend(cv.COMPONENT_SCHEMA) async def to_code(config): meter = cg.new_Pvariable(config[CONF_ID]) driver_type = config[CONF_TYPE].name if CONF_MODE in config: driver_type += ":" + config[CONF_MODE] cg.add( meter.set_meter_params( config[CONF_METER_ID], driver_type, config.get(CONF_KEY, ""), ) ) radio = await cg.get_variable(config[CONF_RADIO_ID]) cg.add(meter.set_radio(radio)) await cg.register_component(meter, config) for conf in config.get(CONF_ON_TELEGRAM, []): trig = cg.new_Pvariable(conf[CONF_TRIGGER_ID], meter) await automation.build_automation( trig, [(MeterPtr, "meter")], conf, ) TELEGRAM_MQTT_PUBLISH_ACTION_SCHEMA = cv.All( MQTT_PUBLISH_ACTION_SCHEMA.extend( { cv.Optional(CONF_PAYLOAD): cv.invalid( "If you want to specify payload, use generic 'mqtt.publish' action" ) } ), lambda c: {**c, CONF_PAYLOAD: cv.Lambda("return meter->as_json();")}, ) automation.register_action( "wmbus_meter.send_telegram_with_mqtt", MQTTPublishAction, TELEGRAM_MQTT_PUBLISH_ACTION_SCHEMA, synchronous=True, )(mqtt_publish_action_to_code)