Add more validation for mysensors (#5493)

* Move isdevice validator under helpers.config_validation.
* Check that all persistence files are set and are unique if any is set
  by user. This is necessary to avoid file name clashes.
* Check that a set persistence file has an existing and writable
  directory.
* Check that a device is either a valid device file, "mqtt", or a valid
  domain name or ip address.
This commit is contained in:
Martin Hjelmare 2017-01-25 07:04:44 +01:00 committed by Paulus Schoutsen
parent b57f5728c5
commit a09a772f43
3 changed files with 61 additions and 20 deletions

View file

@ -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,

View file

@ -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):

View file

@ -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: