Files
esphome-lsm6ds/esphome/components/udp/__init__.py
T

159 lines
5.0 KiB
Python

import hashlib
import esphome.codegen as cg
from esphome.components.api import CONF_ENCRYPTION
from esphome.components.binary_sensor import BinarySensor
from esphome.components.sensor import Sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_BINARY_SENSORS,
CONF_ID,
CONF_INTERNAL,
CONF_KEY,
CONF_NAME,
CONF_PORT,
CONF_SENSORS,
)
from esphome.cpp_generator import MockObjClass
CODEOWNERS = ["@clydebarrow"]
DEPENDENCIES = ["network"]
AUTO_LOAD = ["socket"]
MULTI_CONF = True
udp_ns = cg.esphome_ns.namespace("udp")
UDPComponent = udp_ns.class_("UDPComponent", cg.PollingComponent)
CONF_BROADCAST = "broadcast"
CONF_BROADCAST_ID = "broadcast_id"
CONF_ADDRESSES = "addresses"
CONF_PROVIDER = "provider"
CONF_PROVIDERS = "providers"
CONF_REMOTE_ID = "remote_id"
CONF_UDP_ID = "udp_id"
CONF_PING_PONG_ENABLE = "ping_pong_enable"
CONF_PING_PONG_RECYCLE_TIME = "ping_pong_recycle_time"
CONF_ROLLING_CODE_ENABLE = "rolling_code_enable"
def sensor_validation(cls: MockObjClass):
return cv.maybe_simple_value(
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(cls),
cv.Optional(CONF_BROADCAST_ID): cv.validate_id_name,
}
),
key=CONF_ID,
)
ENCRYPTION_SCHEMA = {
cv.Optional(CONF_ENCRYPTION): cv.maybe_simple_value(
cv.Schema(
{
cv.Required(CONF_KEY): cv.string,
}
),
key=CONF_KEY,
)
}
PROVIDER_SCHEMA = cv.Schema(
{
cv.Required(CONF_NAME): cv.valid_name,
}
).extend(ENCRYPTION_SCHEMA)
def validate_(config):
if CONF_ENCRYPTION in config:
if CONF_SENSORS not in config and CONF_BINARY_SENSORS not in config:
raise cv.Invalid("No sensors or binary sensors to encrypt")
elif config[CONF_ROLLING_CODE_ENABLE]:
raise cv.Invalid("Rolling code requires an encryption key")
if config[CONF_PING_PONG_ENABLE]:
if not any(CONF_ENCRYPTION in p for p in config.get(CONF_PROVIDERS) or ()):
raise cv.Invalid("Ping-pong requires at least one encrypted provider")
return config
CONFIG_SCHEMA = cv.All(
cv.polling_component_schema("15s")
.extend(
{
cv.GenerateID(): cv.declare_id(UDPComponent),
cv.Optional(CONF_PORT, default=18511): cv.port,
cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list(
cv.ipv4
),
cv.Optional(CONF_ROLLING_CODE_ENABLE, default=False): cv.boolean,
cv.Optional(CONF_PING_PONG_ENABLE, default=False): cv.boolean,
cv.Optional(
CONF_PING_PONG_RECYCLE_TIME, default="600s"
): cv.positive_time_period_seconds,
cv.Optional(CONF_SENSORS): cv.ensure_list(sensor_validation(Sensor)),
cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list(
sensor_validation(BinarySensor)
),
cv.Optional(CONF_PROVIDERS): cv.ensure_list(PROVIDER_SCHEMA),
},
)
.extend(ENCRYPTION_SCHEMA),
validate_,
)
SENSOR_SCHEMA = cv.Schema(
{
cv.Optional(CONF_REMOTE_ID): cv.string_strict,
cv.Required(CONF_PROVIDER): cv.valid_name,
cv.GenerateID(CONF_UDP_ID): cv.use_id(UDPComponent),
}
)
def require_internal_with_name(config):
if CONF_NAME in config and CONF_INTERNAL not in config:
raise cv.Invalid("Must provide internal: config when using name:")
return config
def hash_encryption_key(config: dict):
return list(hashlib.sha256(config[CONF_KEY].encode()).digest())
async def to_code(config):
cg.add_define("USE_UDP")
cg.add_global(udp_ns.using)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_port(config[CONF_PORT]))
cg.add(var.set_rolling_code_enable(config[CONF_ROLLING_CODE_ENABLE]))
cg.add(var.set_ping_pong_enable(config[CONF_PING_PONG_ENABLE]))
cg.add(
var.set_ping_pong_recycle_time(
config[CONF_PING_PONG_RECYCLE_TIME].total_seconds
)
)
for sens_conf in config.get(CONF_SENSORS, ()):
sens_id = sens_conf[CONF_ID]
sensor = await cg.get_variable(sens_id)
bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id)
cg.add(var.add_sensor(bcst_id, sensor))
for sens_conf in config.get(CONF_BINARY_SENSORS, ()):
sens_id = sens_conf[CONF_ID]
sensor = await cg.get_variable(sens_id)
bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id)
cg.add(var.add_binary_sensor(bcst_id, sensor))
for address in config[CONF_ADDRESSES]:
cg.add(var.add_address(str(address)))
if encryption := config.get(CONF_ENCRYPTION):
cg.add(var.set_encryption_key(hash_encryption_key(encryption)))
for provider in config.get(CONF_PROVIDERS, ()):
name = provider[CONF_NAME]
cg.add(var.add_provider(name))
if encryption := provider.get(CONF_ENCRYPTION):
cg.add(var.set_provider_encryption(name, hash_encryption_key(encryption)))