diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index 79e572defeb..52b851d1983 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -5,12 +5,15 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.mysensors/ """ import logging +import os import socket import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.bootstrap import setup_component +from homeassistant.components.mqtt import (valid_publish_topic, + valid_subscribe_topic) from homeassistant.const import (ATTR_BATTERY_LEVEL, CONF_NAME, CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON) @@ -44,22 +47,61 @@ REQUIREMENTS = [ 'https://github.com/theolind/pymysensors/archive/' '0b705119389be58332f17753c53167f551254b6c.zip#pymysensors==0.8'] + +def is_socket_address(value): + """Validate that value is a valid address.""" + try: + socket.getaddrinfo(value, None) + return value + except OSError: + raise vol.Invalid('Device is not a valid domain name or ip address') + + +def has_parent_dir(value): + """Validate that value is in an existing directory which is writetable.""" + parent = os.path.dirname(os.path.realpath(value)) + is_dir_writable = os.path.isdir(parent) and os.access(parent, os.W_OK) + if not is_dir_writable: + raise vol.Invalid( + '{} directory does not exist or is not writetable'.format(parent)) + return value + + +def has_all_unique_files(value): + """Validate that all persistence files are unique and set if any is set.""" + persistence_files = [ + gateway.get(CONF_PERSISTENCE_FILE) for gateway in value] + if None in persistence_files and any( + name is not None for name in persistence_files): + raise vol.Invalid( + 'persistence file name of all devices must be set if any is set') + if not all(name is None for name in persistence_files): + schema = vol.Schema(vol.Unique()) + schema(persistence_files) + return value + + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_GATEWAYS): vol.All(cv.ensure_list, [ - { - vol.Required(CONF_DEVICE): cv.string, - vol.Optional(CONF_PERSISTENCE_FILE): cv.string, + vol.Required(CONF_GATEWAYS): vol.All( + cv.ensure_list, has_all_unique_files, + [{ + vol.Required(CONF_DEVICE): + vol.Any(cv.isdevice, MQTT_COMPONENT, is_socket_address), + vol.Optional(CONF_PERSISTENCE_FILE): + vol.All(cv.string, has_parent_dir), vol.Optional( CONF_BAUD_RATE, default=DEFAULT_BAUD_RATE): cv.positive_int, vol.Optional( CONF_TCP_PORT, default=DEFAULT_TCP_PORT): cv.port, - vol.Optional(CONF_TOPIC_IN_PREFIX, default=''): cv.string, - vol.Optional(CONF_TOPIC_OUT_PREFIX, default=''): cv.string, - }, - ]), + vol.Optional( + CONF_TOPIC_IN_PREFIX, default=''): valid_subscribe_topic, + vol.Optional( + CONF_TOPIC_OUT_PREFIX, default=''): valid_publish_topic, + }] + ), vol.Optional(CONF_DEBUG, default=False): cv.boolean, vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, vol.Optional(CONF_PERSISTENCE, default=True): cv.boolean, @@ -100,7 +142,7 @@ def setup(hass, config): out_prefix=out_prefix, retain=retain) else: try: - socket.inet_aton(device) + socket.getaddrinfo(device, None) # valid ip address gateway = mysensors.TCPGateway( device, event_callback=None, persistence=persistence, diff --git a/homeassistant/components/switch/acer_projector.py b/homeassistant/components/switch/acer_projector.py index 7817a127642..416f6431ba5 100644 --- a/homeassistant/components/switch/acer_projector.py +++ b/homeassistant/components/switch/acer_projector.py @@ -4,7 +4,6 @@ Use serial protocol of Acer projector to obtain state of the projector. For more details about this component, please refer to the documentation at https://home-assistant.io/components/switch.acer_projector/ """ -import os import logging import re @@ -47,17 +46,8 @@ CMD_DICT = {LAMP: '* 0 Lamp ?\r', STATE_OFF: '* 0 IR 002\r'} -def isdevice(dev): - """Check if dev a real device.""" - try: - os.stat(dev) - return str(dev) - except OSError: - raise vol.Invalid("No device found!") - - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_FILENAME): isdevice, + vol.Required(CONF_FILENAME): cv.isdevice, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, vol.Optional(CONF_WRITE_TIMEOUT, default=DEFAULT_WRITE_TIMEOUT): diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 0c28dbdd78e..cd26c2779b1 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -70,6 +70,15 @@ def boolean(value: Any) -> bool: return bool(value) +def isdevice(value): + """Validate that value is a real device.""" + try: + os.stat(value) + return str(value) + except OSError: + raise vol.Invalid('No device at {} found'.format(value)) + + def isfile(value: Any) -> str: """Validate that the value is an existing file.""" if value is None: