Make compatible with python 3 (#281)

* Make compatible with python 3

* Update travis

* Env

* Fix typo

* Fix install correct platformio

* Lint

* Fix build flags

* Lint

* Upgrade pylint

* Lint
This commit is contained in:
Otto Winter 2019-01-02 14:11:11 +01:00 committed by GitHub
parent 5db70bea3c
commit 22fd4ec722
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 249 additions and 181 deletions

View File

@ -1,20 +1,30 @@
sudo: false sudo: false
language: python language: python
python:
- "2.7" matrix:
jobs: fast_finish: true
include: include:
- name: "Lint" - python: "2.7"
install: env: TARGET=Lint2.7
- pip install -r requirements.txt install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 tzlocal pillow
- pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow
script: script:
- flake8 esphomeyaml - flake8 esphomeyaml
- pylint esphomeyaml - pylint esphomeyaml
- name: "Test" - python: "3.5.3"
install: env: TARGET=Lint3.5
- pip install -e . install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 tzlocal pillow
- pip install tzlocal pillow script:
- flake8 esphomeyaml
- pylint esphomeyaml
- python: "2.7"
env: TARGET=Test2.7
install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 tzlocal pillow
script:
- esphomeyaml tests/test1.yaml compile
- esphomeyaml tests/test2.yaml compile
- python: "3.5.3"
env: TARGET=Test3.5
install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 tzlocal pillow
script: script:
- esphomeyaml tests/test1.yaml compile - esphomeyaml tests/test1.yaml compile
- esphomeyaml tests/test2.yaml compile - esphomeyaml tests/test2.yaml compile

View File

@ -3,4 +3,4 @@ FROM python:2.7
COPY requirements.txt /requirements.txt COPY requirements.txt /requirements.txt
RUN pip install -r /requirements.txt && \ RUN pip install -r /requirements.txt && \
pip install flake8==3.5.0 pylint==1.9.3 tzlocal pillow pip install flake8==3.6.0 pylint==1.9.4 tzlocal pillow

View File

@ -16,8 +16,9 @@ from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_LOGGER, CON
from esphomeyaml.core import CORE, EsphomeyamlError from esphomeyaml.core import CORE, EsphomeyamlError
from esphomeyaml.cpp_generator import Expression, RawStatement, add, statement from esphomeyaml.cpp_generator import Expression, RawStatement, add, statement
from esphomeyaml.helpers import color, indent from esphomeyaml.helpers import color, indent
from esphomeyaml.storage_json import StorageJSON, storage_path, start_update_check_thread, \ from esphomeyaml.py_compat import safe_input, text_type
esphomeyaml_storage_path from esphomeyaml.storage_json import StorageJSON, esphomeyaml_storage_path, \
start_update_check_thread, storage_path
from esphomeyaml.util import run_external_command, safe_print from esphomeyaml.util import run_external_command, safe_print
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -50,7 +51,7 @@ def choose_prompt(options):
safe_print(u" [{}] {}".format(i + 1, desc)) safe_print(u" [{}] {}".format(i + 1, desc))
while True: while True:
opt = raw_input('(number): ') opt = safe_input('(number): ')
if opt in options: if opt in options:
opt = options.index(opt) opt = options.index(opt)
break break
@ -140,7 +141,7 @@ def write_cpp(config):
if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]: if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]:
if isinstance(exp, Expression) and not exp.required: if isinstance(exp, Expression) and not exp.required:
continue continue
all_code.append(unicode(statement(exp))) all_code.append(text_type(statement(exp)))
writer.write_platformio_project() writer.write_platformio_project()
@ -199,7 +200,7 @@ def upload_program(config, args, host):
if storage is not None and not storage.use_legacy_ota: if storage is not None and not storage.use_legacy_ota:
return res return res
_LOGGER.warn("OTA v2 method failed. Trying with legacy OTA...") _LOGGER.warning("OTA v2 method failed. Trying with legacy OTA...")
return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password, return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password,
CORE.firmware_bin) CORE.firmware_bin)
@ -208,9 +209,9 @@ def show_logs(config, args, port):
if get_port_type(port) == 'SERIAL': if get_port_type(port) == 'SERIAL':
run_miniterm(config, port) run_miniterm(config, port)
return 0 return 0
elif get_port_type(port) == 'NETWORK': if get_port_type(port) == 'NETWORK':
return run_logs(config, port) return run_logs(config, port)
elif get_port_type(port) == 'MQTT': if get_port_type(port) == 'MQTT':
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
raise ValueError raise ValueError

View File

@ -14,6 +14,7 @@ import esphomeyaml.api.api_pb2 as pb
from esphomeyaml.const import CONF_PASSWORD, CONF_PORT from esphomeyaml.const import CONF_PASSWORD, CONF_PORT
from esphomeyaml.core import EsphomeyamlError from esphomeyaml.core import EsphomeyamlError
from esphomeyaml.helpers import resolve_ip_address, indent, color from esphomeyaml.helpers import resolve_ip_address, indent, color
from esphomeyaml.py_compat import text_type
from esphomeyaml.util import safe_print from esphomeyaml.util import safe_print
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -254,14 +255,14 @@ class APIClient(threading.Thread):
def _send_message(self, msg): def _send_message(self, msg):
# type: (message.Message) -> None # type: (message.Message) -> None
for message_type, klass in MESSAGE_TYPE_TO_PROTO.iteritems(): for message_type, klass in MESSAGE_TYPE_TO_PROTO.items():
if isinstance(msg, klass): if isinstance(msg, klass):
break break
else: else:
raise ValueError raise ValueError
encoded = msg.SerializeToString() encoded = msg.SerializeToString()
_LOGGER.debug("Sending %s:\n%s", type(msg), indent(unicode(msg))) _LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg)))
req = chr(0x00) req = chr(0x00)
req += _varuint_to_bytes(len(encoded)) req += _varuint_to_bytes(len(encoded))
req += _varuint_to_bytes(message_type) req += _varuint_to_bytes(message_type)
@ -435,12 +436,14 @@ def run_logs(config, address):
while retry_timer: while retry_timer:
retry_timer.pop(0).cancel() retry_timer.pop(0).cancel()
error = None
try: try:
cli.connect() cli.connect()
cli.login() cli.login()
except APIConnectionError as error: except APIConnectionError as err: # noqa
pass error = err
else:
if error is None:
_LOGGER.info("Successfully connected to %s", address) _LOGGER.info("Successfully connected to %s", address)
return return

View File

@ -41,7 +41,7 @@ def validate_recursive_condition(value):
raise vol.Invalid(u"Unable to find condition with the name '{}', is the " raise vol.Invalid(u"Unable to find condition with the name '{}', is the "
u"component loaded?".format(key), path + [key]) u"component loaded?".format(key), path + [key])
item.setdefault(CONF_CONDITION_ID, None) item.setdefault(CONF_CONDITION_ID, None)
key2 = next((x for x in item if x != CONF_CONDITION_ID and x != key), None) key2 = next((x for x in item if x not in (CONF_CONDITION_ID, key)), None)
if key2 is not None: if key2 is not None:
raise vol.Invalid(u"Cannot have two conditions in one item. Key '{}' overrides '{}'! " raise vol.Invalid(u"Cannot have two conditions in one item. Key '{}' overrides '{}'! "
u"Did you forget to indent the block inside the condition?" u"Did you forget to indent the block inside the condition?"
@ -76,7 +76,7 @@ def validate_recursive_action(value):
raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?" raise vol.Invalid(u"Unable to find action with the name '{}', is the component loaded?"
u"".format(key), path + [key]) u"".format(key), path + [key])
item.setdefault(CONF_ACTION_ID, None) item.setdefault(CONF_ACTION_ID, None)
key2 = next((x for x in item if x != CONF_ACTION_ID and x != key), None) key2 = next((x for x in item if x not in (CONF_ACTION_ID, key)), None)
if key2 is not None: if key2 is not None:
raise vol.Invalid(u"Cannot have two actions in one item. Key '{}' overrides '{}'! " raise vol.Invalid(u"Cannot have two actions in one item. Key '{}' overrides '{}'! "
u"Did you forget to indent the block inside the action?" u"Did you forget to indent the block inside the action?"

View File

@ -43,7 +43,7 @@ BUILD_FLAGS = '-DUSE_API'
def lib_deps(config): def lib_deps(config):
if CORE.is_esp32: if CORE.is_esp32:
return 'AsyncTCP@1.0.1' return 'AsyncTCP@1.0.1'
elif CORE.is_esp8266: if CORE.is_esp8266:
return 'ESPAsyncTCP@1.1.3' return 'ESPAsyncTCP@1.1.3'
raise NotImplementedError raise NotImplementedError

View File

@ -14,6 +14,7 @@ from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import process_lambda, ArrayInitializer, add, Pvariable, \ from esphomeyaml.cpp_generator import process_lambda, ArrayInitializer, add, Pvariable, \
StructInitializer, get_variable StructInitializer, get_variable
from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Trigger, NoArg, Component, App, bool_ from esphomeyaml.cpp_types import esphomelib_ns, Nameable, Trigger, NoArg, Component, App, bool_
from esphomeyaml.py_compat import string_types
DEVICE_CLASSES = [ DEVICE_CLASSES = [
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
@ -70,7 +71,7 @@ MULTI_CLICK_TIMING_SCHEMA = vol.Schema({
def parse_multi_click_timing_str(value): def parse_multi_click_timing_str(value):
if not isinstance(value, basestring): if not isinstance(value, string_types):
return value return value
parts = value.lower().split(' ') parts = value.lower().split(' ')

View File

@ -73,40 +73,40 @@ def receiver_base(full_config):
key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS) key, config = next((k, v) for k, v in full_config.items() if k in REMOTE_KEYS)
if key == CONF_LG: if key == CONF_LG:
return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) return LGReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_NEC: if key == CONF_NEC:
return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) return NECReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_PANASONIC: if key == CONF_PANASONIC:
return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) return PanasonicReceiver.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_SAMSUNG: if key == CONF_SAMSUNG:
return SamsungReceiver.new(name, config[CONF_DATA]) return SamsungReceiver.new(name, config[CONF_DATA])
elif key == CONF_SONY: if key == CONF_SONY:
return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS]) return SonyReceiver.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_RAW: if key == CONF_RAW:
data = ArrayInitializer(*config, multiline=False) data = ArrayInitializer(*config, multiline=False)
return RawReceiver.new(name, data) return RawReceiver.new(name, data)
elif key == CONF_RC_SWITCH_RAW: if key == CONF_RC_SWITCH_RAW:
return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchRawReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_CODE]), len(config[CONF_CODE])) binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
elif key == CONF_RC_SWITCH_TYPE_A: if key == CONF_RC_SWITCH_TYPE_A:
return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeAReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_GROUP]), binary_code(config[CONF_GROUP]),
binary_code(config[CONF_DEVICE]), binary_code(config[CONF_DEVICE]),
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_B: if key == CONF_RC_SWITCH_TYPE_B:
return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeBReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_ADDRESS], config[CONF_CHANNEL],
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_C: if key == CONF_RC_SWITCH_TYPE_C:
return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeCReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_FAMILY][0]) - ord('a'), ord(config[CONF_FAMILY][0]) - ord('a'),
config[CONF_GROUP], config[CONF_DEVICE], config[CONF_GROUP], config[CONF_DEVICE],
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_D: if key == CONF_RC_SWITCH_TYPE_D:
return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeDReceiver.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_GROUP][0]) - ord('a'), ord(config[CONF_GROUP][0]) - ord('a'),
config[CONF_DEVICE], config[CONF_STATE]) config[CONF_DEVICE], config[CONF_STATE])
else:
raise NotImplementedError("Unknown receiver type {}".format(config)) raise NotImplementedError("Unknown receiver type {}".format(config))
def to_code(config): def to_code(config):

View File

@ -33,10 +33,9 @@ def validate_glyphs(value):
if len(x_) < len(y_): if len(x_) < len(y_):
return -1 return -1
elif len(x_) > len(y_): if len(x_) > len(y_):
return 1 return 1
else: raise vol.Invalid(u"Found duplicate glyph {}".format(x))
raise vol.Invalid(u"Found duplicate glyph {}".format(x))
value.sort(cmp=comparator) value.sort(cmp=comparator)
return value return value
@ -47,11 +46,11 @@ def validate_pillow_installed(value):
import PIL import PIL
except ImportError: except ImportError:
raise vol.Invalid("Please install the pillow python package to use this feature. " raise vol.Invalid("Please install the pillow python package to use this feature. "
"(pip2 install pillow)") "(pip install pillow)")
if PIL.__version__[0] < '4': if PIL.__version__[0] < '4':
raise vol.Invalid("Please update your pillow installation to at least 4.0.x. " raise vol.Invalid("Please update your pillow installation to at least 4.0.x. "
"(pip2 install -U pillow)") "(pip install -U pillow)")
return value return value

View File

@ -10,6 +10,8 @@ from esphomeyaml.core import EsphomeyamlError, Lambda, CORE
from esphomeyaml.cpp_generator import Pvariable, RawExpression, add, process_lambda, statement from esphomeyaml.cpp_generator import Pvariable, RawExpression, add, process_lambda, statement
from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns from esphomeyaml.cpp_types import App, Component, esphomelib_ns, global_ns
from esphomeyaml.py_compat import text_type
LOG_LEVELS = { LOG_LEVELS = {
'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE, 'NONE': global_ns.ESPHOMELIB_LOG_LEVEL_NONE,
'ERROR': global_ns.ESPHOMELIB_LOG_LEVEL_ERROR, 'ERROR': global_ns.ESPHOMELIB_LOG_LEVEL_ERROR,
@ -37,7 +39,7 @@ is_log_level = cv.one_of(*LOG_LEVELS, upper=True)
def validate_local_no_higher_than_global(value): def validate_local_no_higher_than_global(value):
global_level = value.get(CONF_LEVEL, 'DEBUG') global_level = value.get(CONF_LEVEL, 'DEBUG')
for tag, level in value.get(CONF_LOGS, {}).iteritems(): for tag, level in value.get(CONF_LOGS, {}).items():
if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level): if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level):
raise EsphomeyamlError(u"The local log level {} for {} must be less severe than the " raise EsphomeyamlError(u"The local log level {} for {} must be less severe than the "
u"global log level {}.".format(level, tag, global_level)) u"global log level {}.".format(level, tag, global_level))
@ -64,7 +66,7 @@ def to_code(config):
add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE])) add(log.set_tx_buffer_size(config[CONF_TX_BUFFER_SIZE]))
if CONF_LEVEL in config: if CONF_LEVEL in config:
add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]])) add(log.set_global_log_level(LOG_LEVELS[config[CONF_LEVEL]]))
for tag, level in config.get(CONF_LOGS, {}).iteritems(): for tag, level in config.get(CONF_LOGS, {}).items():
add(log.set_log_level(tag, LOG_LEVELS[level])) add(log.set_log_level(tag, LOG_LEVELS[level]))
@ -120,7 +122,7 @@ def validate_printf(value):
[cCdiouxXeEfgGaAnpsSZ] # type [cCdiouxXeEfgGaAnpsSZ] # type
) | # OR ) | # OR
%%) # literal "%%" %%) # literal "%%"
""" """ # noqa
matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X)
if len(matches) != len(value[CONF_ARGS]): if len(matches) != len(value[CONF_ARGS]):
raise vol.Invalid(u"Found {} printf-patterns ({}), but {} args were given!" raise vol.Invalid(u"Found {} printf-patterns ({}), but {} args were given!"
@ -140,9 +142,9 @@ LOGGER_LOG_ACTION_SCHEMA = vol.All(maybe_simple_message({
@ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA) @ACTION_REGISTRY.register(CONF_LOGGER_LOG, LOGGER_LOG_ACTION_SCHEMA)
def logger_log_action_to_code(config, action_id, arg_type, template_arg): def logger_log_action_to_code(config, action_id, arg_type, template_arg):
esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]] esp_log = LOG_LEVEL_TO_ESP_LOG[config[CONF_LEVEL]]
args = [RawExpression(unicode(x)) for x in config[CONF_ARGS]] args = [RawExpression(text_type(x)) for x in config[CONF_ARGS]]
text = unicode(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args))) text = text_type(statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args)))
for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')]): for lambda_ in process_lambda(Lambda(text), [(arg_type, 'x')]):
yield None yield None

View File

@ -36,7 +36,7 @@ def get_port(config):
return config[CONF_OTA][CONF_PORT] return config[CONF_OTA][CONF_PORT]
if CORE.is_esp32: if CORE.is_esp32:
return 3232 return 3232
elif CORE.is_esp8266: if CORE.is_esp8266:
return 8266 return 8266
raise NotImplementedError raise NotImplementedError
@ -52,6 +52,6 @@ REQUIRED_BUILD_FLAGS = '-DUSE_NEW_OTA'
def lib_deps(config): def lib_deps(config):
if CORE.is_esp32: if CORE.is_esp32:
return ['Update', 'ESPmDNS'] return ['Update', 'ESPmDNS']
elif CORE.is_esp8266: if CORE.is_esp8266:
return ['Hash', 'ESP8266mDNS'] return ['Hash', 'ESP8266mDNS']
raise NotImplementedError raise NotImplementedError

View File

@ -37,7 +37,7 @@ def validate_custom_output(value):
value[CONF_TYPE] = type value[CONF_TYPE] = type
if type == 'binary': if type == 'binary':
return BINARY_SCHEMA(value) return BINARY_SCHEMA(value)
elif type == 'float': if type == 'float':
return FLOAT_SCHEMA(value) return FLOAT_SCHEMA(value)
raise vol.Invalid("type must either be binary or float, not {}!".format(type)) raise vol.Invalid("type must either be binary or float, not {}!".format(type))

View File

@ -7,6 +7,7 @@ from esphomeyaml.const import CONF_BUFFER_SIZE, CONF_DUMP, CONF_FILTER, CONF_ID,
from esphomeyaml.cpp_generator import Pvariable, add from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component from esphomeyaml.cpp_helpers import gpio_input_pin_expression, setup_component
from esphomeyaml.cpp_types import App, Component, esphomelib_ns from esphomeyaml.cpp_types import App, Component, esphomelib_ns
from esphomeyaml.py_compat import string_types
remote_ns = esphomelib_ns.namespace('remote') remote_ns = esphomelib_ns.namespace('remote')
MULTI_CONF = True MULTI_CONF = True
@ -30,7 +31,7 @@ DUMPERS = {
def validate_dumpers_all(value): def validate_dumpers_all(value):
if not isinstance(value, (str, unicode)): if not isinstance(value, string_types):
raise vol.Invalid("Not valid dumpers") raise vol.Invalid("Not valid dumpers")
if value.upper() == "ALL": if value.upper() == "ALL":
return list(sorted(list(DUMPERS))) return list(sorted(list(DUMPERS)))

View File

@ -10,6 +10,7 @@ from esphomeyaml.core import HexInt
from esphomeyaml.cpp_generator import Pvariable, add from esphomeyaml.cpp_generator import Pvariable, add
from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component from esphomeyaml.cpp_helpers import gpio_output_pin_expression, setup_component
from esphomeyaml.cpp_types import App, Component from esphomeyaml.cpp_types import App, Component
from esphomeyaml.py_compat import text_type
RemoteTransmitterComponent = remote_ns.class_('RemoteTransmitterComponent', RemoteTransmitterComponent = remote_ns.class_('RemoteTransmitterComponent',
RemoteControlComponentBase, Component) RemoteControlComponentBase, Component)
@ -20,7 +21,7 @@ MULTI_CONF = True
def validate_rc_switch_code(value): def validate_rc_switch_code(value):
if not isinstance(value, (str, unicode)): if not isinstance(value, (str, text_type)):
raise vol.Invalid("All RCSwitch codes must be in quotes ('')") raise vol.Invalid("All RCSwitch codes must be in quotes ('')")
for c in value: for c in value:
if c not in ('0', '1'): if c not in ('0', '1'):

View File

@ -6,6 +6,7 @@ import esphomeyaml.config_validation as cv
from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \ from esphomeyaml.const import CONF_ADS1115_ID, CONF_GAIN, CONF_MULTIPLEXER, CONF_NAME, \
CONF_UPDATE_INTERVAL CONF_UPDATE_INTERVAL
from esphomeyaml.cpp_generator import get_variable from esphomeyaml.cpp_generator import get_variable
from esphomeyaml.py_compat import string_types
DEPENDENCIES = ['ads1115'] DEPENDENCIES = ['ads1115']
@ -35,7 +36,7 @@ GAIN = {
def validate_gain(value): def validate_gain(value):
if isinstance(value, float): if isinstance(value, float):
value = u'{:0.03f}'.format(value) value = u'{:0.03f}'.format(value)
elif not isinstance(value, (str, unicode)): elif not isinstance(value, string_types):
raise vol.Invalid('invalid gain "{}"'.format(value)) raise vol.Invalid('invalid gain "{}"'.format(value))
return cv.one_of(*GAIN)(value) return cv.one_of(*GAIN)(value)

View File

@ -36,7 +36,7 @@ SENSORS_TO_TYPE = {
def validate_pmsx003_sensors(value): def validate_pmsx003_sensors(value):
for key, types in SENSORS_TO_TYPE.iteritems(): for key, types in SENSORS_TO_TYPE.items():
if key in value and value[CONF_TYPE] not in types: if key in value and value[CONF_TYPE] not in types:
raise vol.Invalid(u"{} does not have {} sensor!".format(value[CONF_TYPE], key)) raise vol.Invalid(u"{} does not have {} sensor!".format(value[CONF_TYPE], key))
return value return value

View File

@ -35,8 +35,8 @@ def validate_internal_filter(value):
if value.total_microseconds > 13: if value.total_microseconds > 13:
raise vol.Invalid("Maximum internal filter value for ESP32 is 13us") raise vol.Invalid("Maximum internal filter value for ESP32 is 13us")
return value return value
else:
return cv.positive_time_period_microseconds(value) return cv.positive_time_period_microseconds(value)
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({

View File

@ -61,8 +61,8 @@ def _expand_substitutions(substitutions, value, path):
if name.startswith(u'{') and name.endswith(u'}'): if name.startswith(u'{') and name.endswith(u'}'):
name = name[1:-1] name = name[1:-1]
if name not in substitutions: if name not in substitutions:
_LOGGER.warn(u"Found '%s' (see %s) which looks like a substitution, but '%s' was not " _LOGGER.warning(u"Found '%s' (see %s) which looks like a substitution, but '%s' was "
u"declared", orig_value, u'->'.join(str(x) for x in path), name) u"not declared", orig_value, u'->'.join(str(x) for x in path), name)
i = j i = j
continue continue
@ -82,7 +82,7 @@ def _substitute_item(substitutions, item, path):
item[i] = sub item[i] = sub
elif isinstance(item, dict): elif isinstance(item, dict):
replace_keys = [] replace_keys = []
for k, v in item.iteritems(): for k, v in item.items():
if path or k != CONF_SUBSTITUTIONS: if path or k != CONF_SUBSTITUTIONS:
sub = _substitute_item(substitutions, k, path + [k]) sub = _substitute_item(substitutions, k, path + [k])
if sub is not None: if sub is not None:
@ -116,7 +116,7 @@ def do_substitution_pass(config):
key = '' key = ''
try: try:
replace_keys = [] replace_keys = []
for key, value in substitutions.iteritems(): for key, value in substitutions.items():
sub = validate_substitution_key(key) sub = validate_substitution_key(key)
if sub != key: if sub != key:
replace_keys.append((key, sub)) replace_keys.append((key, sub))

View File

@ -85,15 +85,15 @@ def transmitter_base(full_config):
if key == CONF_LG: if key == CONF_LG:
return LGTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) return LGTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_NEC: if key == CONF_NEC:
return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) return NECTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_PANASONIC: if key == CONF_PANASONIC:
return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND]) return PanasonicTransmitter.new(name, config[CONF_ADDRESS], config[CONF_COMMAND])
elif key == CONF_SAMSUNG: if key == CONF_SAMSUNG:
return SamsungTransmitter.new(name, config[CONF_DATA]) return SamsungTransmitter.new(name, config[CONF_DATA])
elif key == CONF_SONY: if key == CONF_SONY:
return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS]) return SonyTransmitter.new(name, config[CONF_DATA], config[CONF_NBITS])
elif key == CONF_RAW: if key == CONF_RAW:
if isinstance(config, dict): if isinstance(config, dict):
data = config[CONF_DATA] data = config[CONF_DATA]
carrier_frequency = config.get(CONF_CARRIER_FREQUENCY) carrier_frequency = config.get(CONF_CARRIER_FREQUENCY)
@ -102,29 +102,29 @@ def transmitter_base(full_config):
carrier_frequency = None carrier_frequency = None
return RawTransmitter.new(name, ArrayInitializer(*data, multiline=False), return RawTransmitter.new(name, ArrayInitializer(*data, multiline=False),
carrier_frequency) carrier_frequency)
elif key == CONF_RC_SWITCH_RAW: if key == CONF_RC_SWITCH_RAW:
return RCSwitchRawTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchRawTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_CODE]), len(config[CONF_CODE])) binary_code(config[CONF_CODE]), len(config[CONF_CODE]))
elif key == CONF_RC_SWITCH_TYPE_A: if key == CONF_RC_SWITCH_TYPE_A:
return RCSwitchTypeATransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeATransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
binary_code(config[CONF_GROUP]), binary_code(config[CONF_GROUP]),
binary_code(config[CONF_DEVICE]), binary_code(config[CONF_DEVICE]),
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_B: if key == CONF_RC_SWITCH_TYPE_B:
return RCSwitchTypeBTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeBTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_ADDRESS], config[CONF_CHANNEL],
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_C: if key == CONF_RC_SWITCH_TYPE_C:
return RCSwitchTypeCTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeCTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_FAMILY][0]) - ord('a'), ord(config[CONF_FAMILY][0]) - ord('a'),
config[CONF_GROUP], config[CONF_DEVICE], config[CONF_GROUP], config[CONF_DEVICE],
config[CONF_STATE]) config[CONF_STATE])
elif key == CONF_RC_SWITCH_TYPE_D: if key == CONF_RC_SWITCH_TYPE_D:
return RCSwitchTypeDTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]), return RCSwitchTypeDTransmitter.new(name, build_rc_switch_protocol(config[CONF_PROTOCOL]),
ord(config[CONF_GROUP][0]) - ord('a'), ord(config[CONF_GROUP][0]) - ord('a'),
config[CONF_DEVICE], config[CONF_STATE]) config[CONF_DEVICE], config[CONF_STATE])
else:
raise NotImplementedError("Unknown transmitter type {}".format(config)) raise NotImplementedError("Unknown transmitter type {}".format(config))
def to_code(config): def to_code(config):

View File

@ -7,6 +7,7 @@ from esphomeyaml.const import CONF_DATA, CONF_INVERTED, CONF_MAKE_ID, CONF_NAME,
from esphomeyaml.core import HexInt from esphomeyaml.core import HexInt
from esphomeyaml.cpp_generator import ArrayInitializer, get_variable, variable from esphomeyaml.cpp_generator import ArrayInitializer, get_variable, variable
from esphomeyaml.cpp_types import App, Application from esphomeyaml.cpp_types import App, Application
from esphomeyaml.py_compat import text_type
DEPENDENCIES = ['uart'] DEPENDENCIES = ['uart']
@ -15,11 +16,11 @@ UARTSwitch = switch.switch_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevic
def validate_data(value): def validate_data(value):
if isinstance(value, unicode): if isinstance(value, text_type):
return value.encode('utf-8') return value.encode('utf-8')
elif isinstance(value, str): if isinstance(value, str):
return value return value
elif isinstance(value, list): if isinstance(value, list):
return vol.Schema([cv.hex_uint8_t])(value) return vol.Schema([cv.hex_uint8_t])(value)
raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes") raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes")

View File

@ -11,6 +11,7 @@ from esphomeyaml.const import CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK,
from esphomeyaml.core import CORE from esphomeyaml.core import CORE
from esphomeyaml.cpp_generator import add, Pvariable, ArrayInitializer from esphomeyaml.cpp_generator import add, Pvariable, ArrayInitializer
from esphomeyaml.cpp_types import esphomelib_ns, Component, NoArg, Trigger, App from esphomeyaml.cpp_types import esphomelib_ns, Component, NoArg, Trigger, App
from esphomeyaml.py_compat import string_types
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -30,9 +31,9 @@ def _tz_timedelta(td):
offset_second = int(abs(td.total_seconds())) % 60 offset_second = int(abs(td.total_seconds())) % 60
if offset_hour == 0 and offset_minute == 0 and offset_second == 0: if offset_hour == 0 and offset_minute == 0 and offset_second == 0:
return '0' return '0'
elif offset_minute == 0 and offset_second == 0: if offset_minute == 0 and offset_second == 0:
return '{}'.format(offset_hour) return '{}'.format(offset_hour)
elif offset_second == 0: if offset_second == 0:
return '{}:{}'.format(offset_hour, offset_minute) return '{}:{}'.format(offset_hour, offset_minute)
return '{}:{}:{}'.format(offset_hour, offset_minute, offset_second) return '{}:{}:{}'.format(offset_hour, offset_minute, offset_second)
@ -119,7 +120,7 @@ def detect_tz():
import pytz import pytz
except ImportError: except ImportError:
raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically " raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically "
"detect the timezone please install tzlocal (pip2 install tzlocal)") "detect the timezone please install tzlocal (pip install tzlocal)")
try: try:
tz = tzlocal.get_localzone() tz = tzlocal.get_localzone()
except pytz.exceptions.UnknownTimeZoneError: except pytz.exceptions.UnknownTimeZoneError:
@ -131,7 +132,7 @@ def detect_tz():
def _parse_cron_int(value, special_mapping, message): def _parse_cron_int(value, special_mapping, message):
special_mapping = special_mapping or {} special_mapping = special_mapping or {}
if isinstance(value, (str, unicode)) and value in special_mapping: if isinstance(value, string_types) and value in special_mapping:
return special_mapping[value] return special_mapping[value]
try: try:
return int(value) return int(value)
@ -140,7 +141,7 @@ def _parse_cron_int(value, special_mapping, message):
def _parse_cron_part(part, min_value, max_value, special_mapping): def _parse_cron_part(part, min_value, max_value, special_mapping):
if part == '*' or part == '?': if part in ('*', '?'):
return set(x for x in range(min_value, max_value + 1)) return set(x for x in range(min_value, max_value + 1))
if '/' in part: if '/' in part:
data = part.split('/') data = part.split('/')

View File

@ -171,6 +171,6 @@ def to_code(config):
def lib_deps(config): def lib_deps(config):
if CORE.is_esp8266: if CORE.is_esp8266:
return 'ESP8266WiFi' return 'ESP8266WiFi'
elif CORE.is_esp32: if CORE.is_esp32:
return None return None
raise NotImplementedError raise NotImplementedError

View File

@ -13,6 +13,7 @@ from esphomeyaml.components import substitutions
from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, ESP_PLATFORMS from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_PLATFORM, ESP_PLATFORMS
from esphomeyaml.core import CORE, EsphomeyamlError from esphomeyaml.core import CORE, EsphomeyamlError
from esphomeyaml.helpers import color, indent from esphomeyaml.helpers import color, indent
from esphomeyaml.py_compat import text_type
from esphomeyaml.util import safe_print from esphomeyaml.util import safe_print
# pylint: disable=unused-import, wrong-import-order # pylint: disable=unused-import, wrong-import-order
@ -50,7 +51,7 @@ def is_platform_component(component):
def iter_components(config): def iter_components(config):
for domain, conf in config.iteritems(): for domain, conf in config.items():
if domain == CONF_ESPHOMEYAML: if domain == CONF_ESPHOMEYAML:
yield CONF_ESPHOMEYAML, core_config, conf yield CONF_ESPHOMEYAML, core_config, conf
continue continue
@ -67,7 +68,7 @@ def iter_components(config):
yield p_name, platform, p_config yield p_name, platform, p_config
ConfigPath = List[Union[basestring, int]] ConfigPath = List[Union[str, int]]
def _path_begins_with_(path, other): # type: (ConfigPath, ConfigPath) -> bool def _path_begins_with_(path, other): # type: (ConfigPath, ConfigPath) -> bool
@ -90,8 +91,8 @@ class Config(OrderedDict):
def add_error(self, message, path): def add_error(self, message, path):
# type: (basestring, ConfigPath) -> None # type: (basestring, ConfigPath) -> None
if not isinstance(message, unicode): if not isinstance(message, text_type):
message = unicode(message) message = text_type(message)
self.errors.append((message, path)) self.errors.append((message, path))
def add_domain(self, path, name): def add_domain(self, path, name):
@ -158,7 +159,7 @@ def iter_ids(config, path=None):
for result in iter_ids(item, path + [i]): for result in iter_ids(item, path + [i]):
yield result yield result
elif isinstance(config, dict): elif isinstance(config, dict):
for key, value in config.iteritems(): for key, value in config.items():
for result in iter_ids(value, path + [key]): for result in iter_ids(value, path + [key]):
yield result yield result
@ -229,7 +230,7 @@ def validate_config(config):
result.add_domain([CONF_ESPHOMEYAML], CONF_ESPHOMEYAML) result.add_domain([CONF_ESPHOMEYAML], CONF_ESPHOMEYAML)
result[CONF_ESPHOMEYAML] = config[CONF_ESPHOMEYAML] result[CONF_ESPHOMEYAML] = config[CONF_ESPHOMEYAML]
for domain, conf in config.iteritems(): for domain, conf in config.items():
domain = str(domain) domain = str(domain)
if domain == CONF_ESPHOMEYAML or domain.startswith(u'.'): if domain == CONF_ESPHOMEYAML or domain.startswith(u'.'):
skip_paths.append([domain]) skip_paths.append([domain])
@ -337,7 +338,7 @@ def validate_config(config):
except vol.Invalid as ex: except vol.Invalid as ex:
_comp_error(ex, [CONF_ESPHOMEYAML]) _comp_error(ex, [CONF_ESPHOMEYAML])
for domain, conf in result.iteritems(): for domain, conf in result.items():
domain = str(domain) domain = str(domain)
if [domain] in skip_paths: if [domain] in skip_paths:
continue continue
@ -401,7 +402,7 @@ def humanize_error(config, validation_error):
offending_item_summary = json.dumps(offending_item_summary) offending_item_summary = json.dumps(offending_item_summary)
except (TypeError, ValueError): except (TypeError, ValueError):
pass pass
validation_error = unicode(validation_error) validation_error = text_type(validation_error)
m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error) m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error)
if m is not None: if m is not None:
validation_error = m.group(1) validation_error = m.group(1)
@ -514,7 +515,7 @@ def dump_dict(config, path, at_root=True):
ret += u'{}' ret += u'{}'
multiline = False multiline = False
for k in conf.iterkeys(): for k in conf.keys():
path_ = path + [k] path_ = path + [k]
error = config.get_error_for_path(path_) error = config.get_error_for_path(path_)
if error is not None: if error is not None:
@ -543,9 +544,9 @@ def dump_dict(config, path, at_root=True):
conf = u'|-\n' + indent(conf) conf = u'|-\n' + indent(conf)
error = config.get_error_for_path(path) error = config.get_error_for_path(path)
col = 'bold_red' if error else 'white' col = 'bold_red' if error else 'white'
ret += color(col, unicode(conf)) ret += color(col, text_type(conf))
elif isinstance(conf, core.Lambda): elif isinstance(conf, core.Lambda):
conf = u'!lambda |-\n' + indent(unicode(conf.value)) conf = u'!lambda |-\n' + indent(text_type(conf.value))
error = config.get_error_for_path(path) error = config.get_error_for_path(path)
col = 'bold_red' if error else 'white' col = 'bold_red' if error else 'white'
ret += color(col, conf) ret += color(col, conf)
@ -554,7 +555,7 @@ def dump_dict(config, path, at_root=True):
else: else:
error = config.get_error_for_path(path) error = config.get_error_for_path(path)
col = 'bold_red' if error else 'white' col = 'bold_red' if error else 'white'
ret += color(col, unicode(conf)) ret += color(col, text_type(conf))
multiline = u'\n' in ret multiline = u'\n' in ret
return ret, multiline return ret, multiline
@ -571,7 +572,7 @@ def strip_default_ids(config):
config.remove(x) config.remove(x)
elif isinstance(config, dict): elif isinstance(config, dict):
to_remove = [] to_remove = []
for k, v in config.iteritems(): for k, v in config.items():
v = config[k] = strip_default_ids(v) v = config[k] = strip_default_ids(v)
if isinstance(v, core.ID) and not v.is_manual: if isinstance(v, core.ID) and not v.is_manual:
to_remove.append(k) to_remove.append(k)

View File

@ -16,6 +16,7 @@ from esphomeyaml.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOV
ESP_PLATFORM_ESP8266 ESP_PLATFORM_ESP8266
from esphomeyaml.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ from esphomeyaml.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
TimePeriodMilliseconds, TimePeriodSeconds TimePeriodMilliseconds, TimePeriodSeconds
from esphomeyaml.py_compat import text_type, string_types, integer_types
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -51,7 +52,7 @@ RESERVED_IDS = [
def alphanumeric(value): def alphanumeric(value):
if value is None: if value is None:
raise vol.Invalid("string value is None") raise vol.Invalid("string value is None")
value = unicode(value) value = text_type(value)
if not value.isalnum(): if not value.isalnum():
raise vol.Invalid("string value is not alphanumeric") raise vol.Invalid("string value is not alphanumeric")
return value return value
@ -70,13 +71,13 @@ def string(value):
if isinstance(value, (dict, list)): if isinstance(value, (dict, list)):
raise vol.Invalid("string value cannot be dictionary or list.") raise vol.Invalid("string value cannot be dictionary or list.")
if value is not None: if value is not None:
return unicode(value) return text_type(value)
raise vol.Invalid("string value is None") raise vol.Invalid("string value is None")
def string_strict(value): def string_strict(value):
"""Strictly only allow strings.""" """Strictly only allow strings."""
if isinstance(value, (str, unicode)): if isinstance(value, string_types):
return value return value
raise vol.Invalid("Must be string, got {}. did you forget putting quotes " raise vol.Invalid("Must be string, got {}. did you forget putting quotes "
"around the value?".format(type(value))) "around the value?".format(type(value)))
@ -138,7 +139,7 @@ def ensure_dict(value):
def hex_int_(value): def hex_int_(value):
if isinstance(value, (int, long)): if isinstance(value, integer_types):
return HexInt(value) return HexInt(value)
value = string_strict(value).lower() value = string_strict(value).lower()
if value.startswith('0x'): if value.startswith('0x'):
@ -147,7 +148,7 @@ def hex_int_(value):
def int_(value): def int_(value):
if isinstance(value, (int, long)): if isinstance(value, integer_types):
return value return value
value = string_strict(value).lower() value = string_strict(value).lower()
if value.startswith('0x'): if value.startswith('0x'):
@ -310,9 +311,9 @@ def time_period_str_colon(value):
def time_period_str_unit(value): def time_period_str_unit(value):
"""Validate and transform time period with time unit and integer value.""" """Validate and transform time period with time unit and integer value."""
if isinstance(value, int): if isinstance(value, int):
raise vol.Invalid("Don't know what '{}' means as it has no time *unit*! Did you mean " raise vol.Invalid("Don't know what '{0}' means as it has no time *unit*! Did you mean "
"'{}s'?".format(value, value)) "'{0}s'?".format(value))
elif not isinstance(value, (str, unicode)): elif not isinstance(value, string_types):
raise vol.Invalid("Expected string for time period with unit.") raise vol.Invalid("Expected string for time period with unit.")
unit_to_kwarg = { unit_to_kwarg = {
@ -489,7 +490,7 @@ def ssid(value):
def ipv4(value): def ipv4(value):
if isinstance(value, list): if isinstance(value, list):
parts = value parts = value
elif isinstance(value, basestring): elif isinstance(value, string_types):
parts = value.split('.') parts = value.split('.')
elif isinstance(value, IPAddress): elif isinstance(value, IPAddress):
return value return value
@ -579,7 +580,7 @@ i2c_address = hex_uint8_t
def percentage(value): def percentage(value):
has_percent_sign = isinstance(value, (str, unicode)) and value.endswith('%') has_percent_sign = isinstance(value, string_types) and value.endswith('%')
if has_percent_sign: if has_percent_sign:
value = float(value[:-1].rstrip()) / 100.0 value = float(value[:-1].rstrip()) / 100.0
if value > 1: if value > 1:
@ -591,7 +592,7 @@ def percentage(value):
def percentage_int(value): def percentage_int(value):
if isinstance(value, (str, unicode)) and value.endswith('%'): if isinstance(value, string_types) and value.endswith('%'):
value = int(value[:-1].rstrip()) value = int(value[:-1].rstrip())
return value return value

View File

@ -13,15 +13,22 @@ from esphomeyaml.helpers import ensure_unique_string
# pylint: disable=unused-import, wrong-import-order # pylint: disable=unused-import, wrong-import-order
from typing import Any, Dict, List # noqa from typing import Any, Dict, List # noqa
from esphomeyaml.py_compat import integer_types, IS_PY2
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class EsphomeyamlError(Exception): class EsphomeyamlError(Exception):
"""General esphomeyaml exception occurred.""" """General esphomeyaml exception occurred."""
pass
class HexInt(long): if IS_PY2:
base_int = long
else:
base_int = int
class HexInt(base_int):
def __str__(self): def __str__(self):
if 0 <= self <= 255: if 0 <= self <= 255:
return "0x{:02X}".format(self) return "0x{:02X}".format(self)
@ -55,7 +62,7 @@ class MACAddress(object):
def is_approximately_integer(value): def is_approximately_integer(value):
if isinstance(value, (int, long)): if isinstance(value, integer_types):
return True return True
return abs(value - round(value)) < 0.001 return abs(value - round(value)) < 0.001
@ -379,7 +386,7 @@ class EsphomeyamlCore(object):
task, domain = self.pending_tasks.popleft() task, domain = self.pending_tasks.popleft()
_LOGGER.debug("Executing task for domain=%s", domain) _LOGGER.debug("Executing task for domain=%s", domain)
try: try:
task.next() next(task)
self.pending_tasks.append((task, domain)) self.pending_tasks.append((task, domain))
except StopIteration: except StopIteration:
_LOGGER.debug(" -> %s finished", domain) _LOGGER.debug(" -> %s finished", domain)
@ -404,7 +411,7 @@ class EsphomeyamlCore(object):
def get_variable_with_full_id(self, id): def get_variable_with_full_id(self, id):
while True: while True:
if id in self.variables: if id in self.variables:
for k, v in self.variables.iteritems(): for k, v in self.variables.items():
if k == id: if k == id:
yield (k, v) yield (k, v)
return return

View File

@ -15,6 +15,7 @@ from esphomeyaml.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266
from esphomeyaml.core import CORE, EsphomeyamlError from esphomeyaml.core import CORE, EsphomeyamlError
from esphomeyaml.cpp_generator import Pvariable, RawExpression, add from esphomeyaml.cpp_generator import Pvariable, RawExpression, add
from esphomeyaml.cpp_types import App, NoArg, const_char_ptr, esphomelib_ns from esphomeyaml.cpp_types import App, NoArg, const_char_ptr, esphomelib_ns
from esphomeyaml.py_compat import text_type
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -52,12 +53,12 @@ def validate_simple_esphomelib_version(value):
CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_REPOSITORY: LIBRARY_URI_REPO,
CONF_TAG: 'v' + ESPHOMELIB_VERSION, CONF_TAG: 'v' + ESPHOMELIB_VERSION,
} }
elif value.upper() == 'DEV': if value.upper() == 'DEV':
return { return {
CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_REPOSITORY: LIBRARY_URI_REPO,
CONF_BRANCH: 'dev' CONF_BRANCH: 'dev'
} }
elif VERSION_REGEX.match(value) is not None: if VERSION_REGEX.match(value) is not None:
return { return {
CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_REPOSITORY: LIBRARY_URI_REPO,
CONF_TAG: 'v' + value, CONF_TAG: 'v' + value,
@ -139,7 +140,7 @@ def validate_arduino_version(value):
if value_ in PLATFORMIO_ESP8266_LUT: if value_ in PLATFORMIO_ESP8266_LUT:
return PLATFORMIO_ESP8266_LUT[value_] return PLATFORMIO_ESP8266_LUT[value_]
return value return value
elif CORE.is_esp32: if CORE.is_esp32:
if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP32_LUT: if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP32_LUT:
raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported " raise vol.Invalid("Unfortunately the arduino framework version '{}' is unsupported "
"at this time. You can override this by manually using " "at this time. You can override this by manually using "
@ -206,7 +207,7 @@ def preload_core_config(config):
CORE.build_path = CORE.relative_path( CORE.build_path = CORE.relative_path(
cv.string(core_conf.get(CONF_BUILD_PATH, default_build_path()))) cv.string(core_conf.get(CONF_BUILD_PATH, default_build_path())))
except vol.Invalid as e: except vol.Invalid as e:
raise EsphomeyamlError(unicode(e)) raise EsphomeyamlError(text_type(e))
def to_code(config): def to_code(config):

View File

@ -7,6 +7,7 @@ from esphomeyaml.helpers import cpp_string_escape, indent_all_but_first_and_last
# pylint: disable=unused-import, wrong-import-order # pylint: disable=unused-import, wrong-import-order
from typing import Any, Generator, List, Optional, Tuple, Union # noqa from typing import Any, Generator, List, Optional, Tuple, Union # noqa
from esphomeyaml.core import ID # noqa from esphomeyaml.core import ID # noqa
from esphomeyaml.py_compat import text_type, string_types, integer_types
class Expression(object): class Expression(object):
@ -28,7 +29,7 @@ class Expression(object):
return self.required return self.required
SafeExpType = Union[Expression, bool, str, unicode, int, long, float, TimePeriod] SafeExpType = Union[Expression, bool, str, text_type, int, float, TimePeriod]
class RawExpression(Expression): class RawExpression(Expression):
@ -73,7 +74,7 @@ class ExpressionList(Expression):
self.args.append(exp) self.args.append(exp)
def __str__(self): def __str__(self):
text = u", ".join(unicode(x) for x in self.args) text = u", ".join(text_type(x) for x in self.args)
return indent_all_but_first_and_last(text) return indent_all_but_first_and_last(text)
@ -115,7 +116,7 @@ class StructInitializer(Expression):
if not isinstance(args, OrderedDict): if not isinstance(args, OrderedDict):
args = OrderedDict(args) args = OrderedDict(args)
self.args = OrderedDict() self.args = OrderedDict()
for key, value in args.iteritems(): for key, value in args.items():
if value is None: if value is None:
continue continue
exp = safe_exp(value) exp = safe_exp(value)
@ -124,7 +125,7 @@ class StructInitializer(Expression):
def __str__(self): def __str__(self):
cpp = u'{}{{\n'.format(self.base) cpp = u'{}{{\n'.format(self.base)
for key, value in self.args.iteritems(): for key, value in self.args.items():
cpp += u' .{} = {},\n'.format(key, value) cpp += u' .{} = {},\n'.format(key, value)
cpp += u'}' cpp += u'}'
return cpp return cpp
@ -176,7 +177,7 @@ class ParameterListExpression(Expression):
self.requires.append(parameter) self.requires.append(parameter)
def __str__(self): def __str__(self):
return u", ".join(unicode(x) for x in self.parameters) return u", ".join(text_type(x) for x in self.parameters)
class LambdaExpression(Expression): class LambdaExpression(Expression):
@ -203,7 +204,7 @@ class LambdaExpression(Expression):
@property @property
def content(self): def content(self):
return u''.join(unicode(part) for part in self.parts) return u''.join(text_type(part) for part in self.parts)
class Literal(Expression): class Literal(Expression):
@ -232,7 +233,7 @@ class IntLiteral(Literal):
return u'{}UL'.format(self.i) return u'{}UL'.format(self.i)
if self.i < -2147483648: if self.i < -2147483648:
return u'{}LL'.format(self.i) return u'{}LL'.format(self.i)
return unicode(self.i) return text_type(self.i)
class BoolLiteral(Literal): class BoolLiteral(Literal):
@ -268,21 +269,21 @@ def safe_exp(obj # type: Union[Expression, bool, str, unicode, int, long, float
# type: (...) -> Expression # type: (...) -> Expression
if isinstance(obj, Expression): if isinstance(obj, Expression):
return obj return obj
elif isinstance(obj, bool): if isinstance(obj, bool):
return BoolLiteral(obj) return BoolLiteral(obj)
elif isinstance(obj, (str, unicode)): if isinstance(obj, string_types):
return StringLiteral(obj) return StringLiteral(obj)
elif isinstance(obj, HexInt): if isinstance(obj, HexInt):
return HexIntLiteral(obj) return HexIntLiteral(obj)
elif isinstance(obj, (int, long)): if isinstance(obj, integer_types):
return IntLiteral(obj) return IntLiteral(obj)
elif isinstance(obj, float): if isinstance(obj, float):
return FloatLiteral(obj) return FloatLiteral(obj)
elif isinstance(obj, TimePeriodMicroseconds): if isinstance(obj, TimePeriodMicroseconds):
return IntLiteral(int(obj.total_microseconds)) return IntLiteral(int(obj.total_microseconds))
elif isinstance(obj, TimePeriodMilliseconds): if isinstance(obj, TimePeriodMilliseconds):
return IntLiteral(int(obj.total_milliseconds)) return IntLiteral(int(obj.total_milliseconds))
elif isinstance(obj, TimePeriodSeconds): if isinstance(obj, TimePeriodSeconds):
return IntLiteral(int(obj.total_seconds)) return IntLiteral(int(obj.total_seconds))
raise ValueError(u"Object is not an expression", obj) raise ValueError(u"Object is not an expression", obj)
@ -441,7 +442,7 @@ class MockObj(Expression):
return obj return obj
def __str__(self): # type: () -> unicode def __str__(self): # type: () -> unicode
return unicode(self.base) return text_type(self.base)
def require(self): # type: () -> None def require(self): # type: () -> None
self.required = True self.required = True

View File

@ -20,11 +20,11 @@ def generic_gpio_pin_expression_(conf, mock_obj, default_mode):
mode = pcf8574.PCF8675_GPIO_MODES[conf.get(CONF_MODE, u'INPUT')] mode = pcf8574.PCF8675_GPIO_MODES[conf.get(CONF_MODE, u'INPUT')]
yield hub.make_input_pin(number, mode, inverted) yield hub.make_input_pin(number, mode, inverted)
return return
elif default_mode == u'OUTPUT': if default_mode == u'OUTPUT':
yield hub.make_output_pin(number, inverted) yield hub.make_output_pin(number, inverted)
return return
else:
raise EsphomeyamlError(u"Unknown default mode {}".format(default_mode)) raise EsphomeyamlError(u"Unknown default mode {}".format(default_mode))
if len(conf) == 1: if len(conf) == 1:
yield IntLiteral(number) yield IntLiteral(number)
return return

View File

@ -176,7 +176,7 @@ class WizardRequestHandler(BaseHandler):
if not self.is_authenticated(): if not self.is_authenticated():
self.redirect('/login') self.redirect('/login')
return return
kwargs = {k: ''.join(v) for k, v in self.request.arguments.iteritems()} kwargs = {k: ''.join(v) for k, v in self.request.arguments.items()}
destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml') destination = os.path.join(CONFIG_DIR, kwargs['name'] + '.yaml')
wizard.wizard_write(path=destination, **kwargs) wizard.wizard_write(path=destination, **kwargs)
self.redirect('/?begin=True') self.redirect('/?begin=True')
@ -436,7 +436,7 @@ class LoginHandler(BaseHandler):
self.redirect('/') self.redirect('/')
return return
except Exception as err: # pylint: disable=broad-except except Exception as err: # pylint: disable=broad-except
_LOGGER.warn("Error during Hass.io auth request: %s", err) _LOGGER.warning("Error during Hass.io auth request: %s", err)
self.set_status(500) self.set_status(500)
self.render_hassio_login(error="Internal server error") self.render_hassio_login(error="Internal server error")
return return

View File

@ -37,6 +37,7 @@
import hashlib import hashlib
import logging import logging
# pylint: disable=deprecated-module
import optparse import optparse
import os import os
import random import random

View File

@ -6,6 +6,8 @@ import os
import socket import socket
import subprocess import subprocess
from esphomeyaml.py_compat import text_type, IS_PY2
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -39,14 +41,16 @@ def indent(text, padding=u' '):
# From https://stackoverflow.com/a/14945195/8924614 # From https://stackoverflow.com/a/14945195/8924614
def cpp_string_escape(string, encoding='utf-8'): def cpp_string_escape(string, encoding='utf-8'):
if isinstance(string, unicode): if isinstance(string, text_type):
string = string.encode(encoding) string = string.encode(encoding)
result = '' result = ''
for character in string: for character in string:
if not (32 <= ord(character) < 127) or character in ('\\', '"'): if IS_PY2:
result += '\\%03o' % ord(character) character = ord(character)
if not (32 <= character < 127) or character in ('\\', '"'):
result += '\\%03o' % character
else: else:
result += character result += chr(character)
return '"' + result + '"' return '"' + result + '"'

View File

@ -246,7 +246,7 @@ def validate_gpio_pin(value):
_LOGGER.warning(u"ESP32: Pin %s (20, 24, 28-31) can usually not be used. " _LOGGER.warning(u"ESP32: Pin %s (20, 24, 28-31) can usually not be used. "
u"Be warned.", value) u"Be warned.", value)
return value return value
elif CORE.is_esp8266: if CORE.is_esp8266:
if 6 <= value <= 11: if 6 <= value <= 11:
_LOGGER.warning(u"ESP8266: Pin %s (6-11) might already be used by the " _LOGGER.warning(u"ESP8266: Pin %s (6-11) might already be used by the "
u"flash interface. Be warned.", value) u"flash interface. Be warned.", value)
@ -264,7 +264,7 @@ def input_pullup_pin(value):
value = input_pin(value) value = input_pin(value)
if CORE.is_esp32: if CORE.is_esp32:
return output_pin(value) return output_pin(value)
elif CORE.is_esp8266: if CORE.is_esp8266:
if value == 0: if value == 0:
raise vol.Invalid("GPIO Pin 0 does not support pullup pin mode. " raise vol.Invalid("GPIO Pin 0 does not support pullup pin mode. "
"Please choose another pin.") "Please choose another pin.")
@ -279,7 +279,7 @@ def output_pin(value):
raise vol.Invalid(u"ESP32: GPIO{} (34-39) can only be used as an " raise vol.Invalid(u"ESP32: GPIO{} (34-39) can only be used as an "
u"input pin.".format(value)) u"input pin.".format(value))
return value return value
elif CORE.is_esp8266: if CORE.is_esp8266:
return value return value
raise NotImplementedError raise NotImplementedError
@ -316,7 +316,7 @@ PIN_MODES_ESP32 = [
def pin_mode(value): def pin_mode(value):
if CORE.is_esp32: if CORE.is_esp32:
return cv.one_of(*PIN_MODES_ESP32, upper=True)(value) return cv.one_of(*PIN_MODES_ESP32, upper=True)(value)
elif CORE.is_esp8266: if CORE.is_esp8266:
return cv.one_of(*PIN_MODES_ESP8266, upper=True)(value) return cv.one_of(*PIN_MODES_ESP8266, upper=True)(value)
raise NotImplementedError raise NotImplementedError

25
esphomeyaml/py_compat.py Normal file
View File

@ -0,0 +1,25 @@
import sys
PYTHON_MAJOR = sys.version_info[0]
IS_PY2 = PYTHON_MAJOR == 2
IS_PY3 = PYTHON_MAJOR == 3
# pylint: disable=no-else-return
def safe_input(line):
if IS_PY2:
return raw_input(line)
else:
return input(line)
if IS_PY2:
text_type = unicode
string_types = (str, unicode)
integer_types = (int, long)
binary_type = str
else:
text_type = str
string_types = (str,)
integer_types = (int,)
binary_type = bytes

View File

@ -272,13 +272,13 @@ class CheckForUpdateThread(threading.Thread):
remote_version = StrictVersion(storage.remote_version) remote_version = StrictVersion(storage.remote_version)
self_version = StrictVersion(const.__version__) self_version = StrictVersion(const.__version__)
if remote_version > self_version: if remote_version > self_version:
_LOGGER.warn("*" * 80) _LOGGER.warning("*" * 80)
_LOGGER.warn("A new version of esphomeyaml is available: %s (this is %s)", _LOGGER.warning("A new version of esphomeyaml is available: %s (this is %s)",
self.format_version(remote_version), self.format_version(self_version)) self.format_version(remote_version), self.format_version(self_version))
_LOGGER.warn("Changelog: %s/esphomeyaml/changelog/index.html", self.docs_base) _LOGGER.warning("Changelog: %s/esphomeyaml/changelog/index.html", self.docs_base)
_LOGGER.warn("Update Instructions: %s/esphomeyaml/guides/faq.html" _LOGGER.warning("Update Instructions: %s/esphomeyaml/guides/faq.html"
"#how-do-i-update-to-the-latest-version", self.docs_base) "#how-do-i-update-to-the-latest-version", self.docs_base)
_LOGGER.warn("*" * 80) _LOGGER.warning("*" * 80)
def run(self): def run(self):
try: try:

View File

@ -11,31 +11,32 @@ from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_PLATFORM_ES
from esphomeyaml.helpers import color from esphomeyaml.helpers import color
# pylint: disable=anomalous-backslash-in-string # pylint: disable=anomalous-backslash-in-string
from esphomeyaml.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS from esphomeyaml.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS
from esphomeyaml.py_compat import safe_input, text_type
from esphomeyaml.storage_json import StorageJSON, ext_storage_path from esphomeyaml.storage_json import StorageJSON, ext_storage_path
from esphomeyaml.util import safe_print from esphomeyaml.util import safe_print
CORE_BIG = """ _____ ____ _____ ______ CORE_BIG = r""" _____ ____ _____ ______
/ ____/ __ \| __ \| ____| / ____/ __ \| __ \| ____|
| | | | | | |__) | |__ | | | | | | |__) | |__
| | | | | | _ /| __| | | | | | | _ /| __|
| |___| |__| | | \ \| |____ | |___| |__| | | \ \| |____
\_____\____/|_| \_\______| \_____\____/|_| \_\______|
""" """
ESP_BIG = """ ______ _____ _____ ESP_BIG = r""" ______ _____ _____
| ____|/ ____| __ \\ | ____|/ ____| __ \\
| |__ | (___ | |__) | | |__ | (___ | |__) |
| __| \___ \| ___/ | __| \___ \| ___/
| |____ ____) | | | |____ ____) | |
|______|_____/|_| |______|_____/|_|
""" """
WIFI_BIG = """ __ ___ ______ _ WIFI_BIG = r""" __ ___ ______ _
\ \ / (_) ____(_) \ \ / (_) ____(_)
\ \ /\ / / _| |__ _ \ \ /\ / / _| |__ _
\ \/ \/ / | | __| | | \ \/ \/ / | | __| | |
\ /\ / | | | | | \ /\ / | | | | |
\/ \/ |_|_| |_| \/ \/ |_|_| |_|
""" """
OTA_BIG = """ ____ _______ OTA_BIG = r""" ____ _______
/ __ \__ __|/\\ / __ \__ __|/\\
| | | | | | / \\ | | | | | | / \\
| | | | | | / /\ \\ | | | | | | / /\ \\
@ -85,7 +86,7 @@ def wizard_write(path, **kwargs):
storage.save(storage_path) storage.save(storage_path)
if os.getenv('ESPHOMEYAML_QUICKWIZARD', False): if os.getenv('ESPHOMEYAML_QUICKWIZARD', ''):
def sleep(time): def sleep(time):
pass pass
else: else:
@ -104,12 +105,12 @@ def safe_print_step(step, big):
def default_input(text, default): def default_input(text, default):
safe_print() safe_print()
safe_print(u"Press ENTER for default ({})".format(default)) safe_print(u"Press ENTER for default ({})".format(default))
return raw_input(text.format(default)) or default return safe_input(text.format(default)) or default
# From https://stackoverflow.com/a/518232/8924614 # From https://stackoverflow.com/a/518232/8924614
def strip_accents(string): def strip_accents(string):
return u''.join(c for c in unicodedata.normalize('NFD', unicode(string)) return u''.join(c for c in unicodedata.normalize('NFD', text_type(string))
if unicodedata.category(c) != 'Mn') if unicodedata.category(c) != 'Mn')
@ -140,7 +141,7 @@ def wizard(path):
color('bold_white', "livingroom"))) color('bold_white', "livingroom")))
safe_print() safe_print()
sleep(1) sleep(1)
name = raw_input(color("bold_white", "(name): ")) name = safe_input(color("bold_white", "(name): "))
while True: while True:
try: try:
name = cv.valid_name(name) name = cv.valid_name(name)
@ -165,7 +166,7 @@ def wizard(path):
sleep(0.5) sleep(0.5)
safe_print() safe_print()
safe_print("Please enter either ESP32 or ESP8266.") safe_print("Please enter either ESP32 or ESP8266.")
platform = raw_input(color("bold_white", "(ESP32/ESP8266): ")) platform = safe_input(color("bold_white", "(ESP32/ESP8266): "))
try: try:
platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform) platform = vol.All(vol.Upper, vol.Any(*ESP_PLATFORMS))(platform)
break break
@ -197,7 +198,7 @@ def wizard(path):
safe_print("Options: {}".format(', '.join(boards))) safe_print("Options: {}".format(', '.join(boards)))
while True: while True:
board = raw_input(color("bold_white", "(board): ")) board = safe_input(color("bold_white", "(board): "))
try: try:
board = vol.All(vol.Lower, vol.Any(*boards))(board) board = vol.All(vol.Lower, vol.Any(*boards))(board)
break break
@ -221,7 +222,7 @@ def wizard(path):
sleep(1.5) sleep(1.5)
safe_print("For example \"{}\".".format(color('bold_white', "Abraham Linksys"))) safe_print("For example \"{}\".".format(color('bold_white', "Abraham Linksys")))
while True: while True:
ssid = raw_input(color('bold_white', "(ssid): ")) ssid = safe_input(color('bold_white', "(ssid): "))
try: try:
ssid = cv.ssid(ssid) ssid = cv.ssid(ssid)
break break
@ -241,7 +242,7 @@ def wizard(path):
safe_print() safe_print()
safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42'))) safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42')))
sleep(0.5) sleep(0.5)
psk = raw_input(color('bold_white', '(PSK): ')) psk = safe_input(color('bold_white', '(PSK): '))
safe_print("Perfect! WiFi is now set up (you can create static IPs and so on later).") safe_print("Perfect! WiFi is now set up (you can create static IPs and so on later).")
sleep(1.5) sleep(1.5)
@ -253,7 +254,7 @@ def wizard(path):
safe_print() safe_print()
sleep(0.25) sleep(0.25)
safe_print("Press ENTER for no password") safe_print("Press ENTER for no password")
password = raw_input(color('bold_white', '(password): ')) password = safe_input(color('bold_white', '(password): '))
wizard_write(path=path, name=name, platform=platform, board=board, wizard_write(path=path, name=name, platform=platform, board=board,
ssid=ssid, psk=psk, password=password) ssid=ssid, psk=psk, password=password)

View File

@ -16,6 +16,7 @@ from esphomeyaml.core import CORE, EsphomeyamlError
from esphomeyaml.core_config import VERSION_REGEX, LIBRARY_URI_REPO, GITHUB_ARCHIVE_ZIP from esphomeyaml.core_config import VERSION_REGEX, LIBRARY_URI_REPO, GITHUB_ARCHIVE_ZIP
from esphomeyaml.helpers import mkdir_p, run_system_command from esphomeyaml.helpers import mkdir_p, run_system_command
from esphomeyaml.pins import ESP8266_LD_SCRIPTS, ESP8266_FLASH_SIZES from esphomeyaml.pins import ESP8266_LD_SCRIPTS, ESP8266_FLASH_SIZES
from esphomeyaml.py_compat import IS_PY3, string_types
from esphomeyaml.storage_json import StorageJSON, storage_path from esphomeyaml.storage_json import StorageJSON, storage_path
from esphomeyaml.util import safe_print from esphomeyaml.util import safe_print
@ -71,7 +72,7 @@ def get_build_flags(key):
flags = flags(conf) flags = flags(conf)
if flags is None: if flags is None:
continue continue
if isinstance(flags, (str, unicode)): if isinstance(flags, string_types):
flags = [flags] flags = [flags]
build_flags |= set(flags) build_flags |= set(flags)
return build_flags return build_flags
@ -114,14 +115,19 @@ def update_esphomelib_repo():
'--') '--')
if rc != 0: if rc != 0:
# local changes, cannot update # local changes, cannot update
_LOGGER.warn("Local changes in esphomelib copy from git. Will not auto-update.") _LOGGER.warning("Local changes in esphomelib copy from git. Will not auto-update.")
return return
_LOGGER.info("Updating esphomelib copy from git (%s)", esphomelib_path) _LOGGER.info("Updating esphomelib copy from git (%s)", esphomelib_path)
rc, stdout, _ = run_system_command('git', '-c', 'color.ui=always', '-C', esphomelib_path, rc, stdout, _ = run_system_command('git', '-c', 'color.ui=always', '-C', esphomelib_path,
'pull', '--stat') 'pull', '--stat')
if rc != 0: if rc != 0:
_LOGGER.warn("Couldn't auto-update local git copy of esphomelib.") _LOGGER.warning("Couldn't auto-update local git copy of esphomelib.")
return return
if IS_PY3:
try:
stdout = stdout.encode('utf-8')
except Exception: # pylint: disable=broad-except
pass
safe_print(stdout.strip()) safe_print(stdout.strip())

View File

@ -12,6 +12,7 @@ import yaml.constructor
from esphomeyaml import core from esphomeyaml import core
from esphomeyaml.core import EsphomeyamlError, HexInt, IPAddress, Lambda, MACAddress, TimePeriod from esphomeyaml.core import EsphomeyamlError, HexInt, IPAddress, Lambda, MACAddress, TimePeriod
from esphomeyaml.py_compat import text_type, string_types
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -24,14 +25,10 @@ SECRET_YAML = u'secrets.yaml'
class NodeListClass(list): class NodeListClass(list):
"""Wrapper class to be able to add attributes on a list.""" """Wrapper class to be able to add attributes on a list."""
pass
class NodeStrClass(text_type):
class NodeStrClass(unicode):
"""Wrapper class to be able to add attributes on a string.""" """Wrapper class to be able to add attributes on a string."""
pass
class SafeLineLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors class SafeLineLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
"""Loader class that keeps track of line numbers.""" """Loader class that keeps track of line numbers."""
@ -72,7 +69,7 @@ def custom_construct_pairs(loader, node):
if not isinstance(obj, dict): if not isinstance(obj, dict):
raise EsphomeyamlError( raise EsphomeyamlError(
"Expected mapping for anchored include tag, got {}".format(type(obj))) "Expected mapping for anchored include tag, got {}".format(type(obj)))
for key, value in obj.iteritems(): for key, value in obj.items():
pairs.append((key, value)) pairs.append((key, value))
else: else:
key_node, value_node = kv key_node, value_node = kv
@ -168,7 +165,7 @@ def _construct_seq(loader, node):
def _add_reference(obj, loader, node): def _add_reference(obj, loader, node):
"""Add file reference information to an object.""" """Add file reference information to an object."""
if isinstance(obj, (str, unicode)): if isinstance(obj, string_types):
obj = NodeStrClass(obj) obj = NodeStrClass(obj)
if isinstance(obj, list): if isinstance(obj, list):
obj = NodeListClass(obj) obj = NodeListClass(obj)
@ -184,7 +181,7 @@ def _env_var_yaml(_, node):
# Check for a default value # Check for a default value
if len(args) > 1: if len(args) > 1:
return os.getenv(args[0], u' '.join(args[1:])) return os.getenv(args[0], u' '.join(args[1:]))
elif args[0] in os.environ: if args[0] in os.environ:
return os.environ[args[0]] return os.environ[args[0]]
raise EsphomeyamlError(u"Environment variable {} not defined.".format(node.value)) raise EsphomeyamlError(u"Environment variable {} not defined.".format(node.value))
@ -268,7 +265,7 @@ def _secret_yaml(loader, node):
def _lambda(loader, node): def _lambda(loader, node):
return Lambda(unicode(node.value)) return Lambda(text_type(node.value))
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict) yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict)
@ -295,7 +292,7 @@ def represent_odict(dump, tag, mapping, flow_style=None):
dump.represented_objects[dump.alias_key] = node dump.represented_objects[dump.alias_key] = node
best_style = True best_style = True
if hasattr(mapping, 'items'): if hasattr(mapping, 'items'):
mapping = mapping.items() mapping = list(mapping.items())
for item_key, item_value in mapping: for item_key, item_value in mapping:
node_key = dump.represent_data(item_key) node_key = dump.represent_data(item_key)
node_value = dump.represent_data(item_value) node_value = dump.represent_data(item_value)
@ -372,7 +369,7 @@ yaml.SafeDumper.add_representer(
dumper.represent_sequence('tag:yaml.org,2002:seq', value) dumper.represent_sequence('tag:yaml.org,2002:seq', value)
) )
yaml.SafeDumper.add_representer(unicode, unicode_representer) yaml.SafeDumper.add_representer(text_type, unicode_representer)
yaml.SafeDumper.add_representer(HexInt, hex_int_representer) yaml.SafeDumper.add_representer(HexInt, hex_int_representer)
yaml.SafeDumper.add_representer(IPAddress, stringify_representer) yaml.SafeDumper.add_representer(IPAddress, stringify_representer)
yaml.SafeDumper.add_representer(MACAddress, stringify_representer) yaml.SafeDumper.add_representer(MACAddress, stringify_representer)

View File

@ -19,6 +19,8 @@ disable=
cyclic-import, cyclic-import,
redefined-builtin, redefined-builtin,
undefined-loop-variable, undefined-loop-variable,
useless-object-inheritance,
stop-iteration-return,
additional-builtins= additional-builtins=

View File

@ -56,7 +56,7 @@ setup(
zip_safe=False, zip_safe=False,
platforms='any', platforms='any',
test_suite='tests', test_suite='tests',
python_requires='>=2.7,<3', python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,<4.0',
install_requires=REQUIRES, install_requires=REQUIRES,
keywords=['home', 'automation'], keywords=['home', 'automation'],
entry_points={ entry_points={