mirror of
https://github.com/esphome/esphome.git
synced 2025-11-13 21:35:48 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c31c5c4041 | ||
|
|
34605f19ee | ||
|
|
58e1b8454d | ||
|
|
0ab63dc4d4 | ||
|
|
51c856e65e |
10
.travis.yml
Normal file
10
.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
sudo: false
|
||||||
|
language: python
|
||||||
|
python:
|
||||||
|
- "2.7"
|
||||||
|
install:
|
||||||
|
- pip install -r requirements.txt
|
||||||
|
- pip install flake8==3.5.0 pylint==1.8.4
|
||||||
|
script:
|
||||||
|
- flake8 esphomeyaml
|
||||||
|
- pylint esphomeyaml
|
||||||
@@ -11,7 +11,7 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
|
|
||||||
COPY docker/platformio.ini /usr/src/app/
|
COPY docker/platformio.ini /usr/src/app/
|
||||||
RUN platformio settings set enable_telemetry No && \
|
RUN platformio settings set enable_telemetry No && \
|
||||||
platformio lib --global install esphomelib=https://github.com/OttoWinter/esphomelib.git#v1.2.1 && \
|
platformio lib --global install esphomelib@1.2.1 && \
|
||||||
platformio run -e espressif32 -e espressif8266; exit 0
|
platformio run -e espressif32 -e espressif8266; exit 0
|
||||||
|
|
||||||
# Fix issue with static IP on ESP32: https://github.com/espressif/arduino-esp32/issues/1081
|
# Fix issue with static IP on ESP32: https://github.com/espressif/arduino-esp32/issues/1081
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from esphomeyaml.const import CONF_ESPHOMEYAML, CONF_HOSTNAME, CONF_MANUAL_IP, C
|
|||||||
CONF_STATIC_IP, \
|
CONF_STATIC_IP, \
|
||||||
CONF_WIFI, CONF_LOGGER, CONF_BAUD_RATE
|
CONF_WIFI, CONF_LOGGER, CONF_BAUD_RATE
|
||||||
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, \
|
from esphomeyaml.helpers import AssignmentExpression, RawStatement, _EXPRESSIONS, add, \
|
||||||
get_variable, indent, quote, statement
|
get_variable, indent, quote, statement, color
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -36,21 +36,44 @@ def discover_serial_ports():
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
result = None
|
result = []
|
||||||
for p, d, h in comports():
|
descs = []
|
||||||
if not p:
|
for port, desc, info in comports():
|
||||||
|
if not port:
|
||||||
continue
|
continue
|
||||||
if "VID:PID" in h:
|
if "VID:PID" in info:
|
||||||
if result is not None:
|
result.append(port)
|
||||||
return None
|
descs.append(desc)
|
||||||
result = p
|
|
||||||
|
|
||||||
return result
|
if not result:
|
||||||
|
return None
|
||||||
|
if len(result) == 1:
|
||||||
|
return result[0]
|
||||||
|
print(u"Found multiple serial port options, please choose one:")
|
||||||
|
for i, (res, desc) in enumerate(zip(result, descs)):
|
||||||
|
print(u" [{}] {} ({})".format(i, res, desc))
|
||||||
|
print(u" [{}] Over The Air".format(len(result)))
|
||||||
|
print()
|
||||||
|
while True:
|
||||||
|
opt = raw_input('(number): ')
|
||||||
|
if opt in result:
|
||||||
|
opt = result.index(opt)
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
opt = int(opt)
|
||||||
|
if opt < 0 or opt > len(result):
|
||||||
|
raise ValueError
|
||||||
|
break
|
||||||
|
except ValueError:
|
||||||
|
print(color('red', u"Invalid option: '{}'".format(opt)))
|
||||||
|
if opt == len(result):
|
||||||
|
return None
|
||||||
|
return result[opt]
|
||||||
|
|
||||||
|
|
||||||
def run_platformio(*cmd):
|
def run_platformio(*cmd):
|
||||||
def mock_exit(rc):
|
def mock_exit(return_code):
|
||||||
raise SystemExit(rc)
|
raise SystemExit(return_code)
|
||||||
|
|
||||||
orig_argv = sys.argv
|
orig_argv = sys.argv
|
||||||
orig_exit = sys.exit # mock sys.exit
|
orig_exit = sys.exit # mock sys.exit
|
||||||
@@ -63,10 +86,10 @@ def run_platformio(*cmd):
|
|||||||
return platformio.__main__.main()
|
return platformio.__main__.main()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
return 1
|
return 1
|
||||||
except SystemExit as e:
|
except SystemExit as err:
|
||||||
return e.args[0]
|
return err.args[0]
|
||||||
except Exception as e:
|
except Exception as err: # pylint: disable=broad-except
|
||||||
_LOGGER.error(u"Running platformio failed: %s", e)
|
_LOGGER.error(u"Running platformio failed: %s", err)
|
||||||
_LOGGER.error(u"Please try running %s locally.", full_cmd)
|
_LOGGER.error(u"Please try running %s locally.", full_cmd)
|
||||||
finally:
|
finally:
|
||||||
sys.argv = orig_argv
|
sys.argv = orig_argv
|
||||||
@@ -125,16 +148,7 @@ def compile_program(config):
|
|||||||
|
|
||||||
def upload_program(config, args, port):
|
def upload_program(config, args, port):
|
||||||
_LOGGER.info("Uploading binary...")
|
_LOGGER.info("Uploading binary...")
|
||||||
if args.upload_port is not None:
|
|
||||||
if args.upload_port == 'HELLO':
|
|
||||||
return run_platformio('platformio', 'run', '-d', get_base_path(config),
|
|
||||||
'-t', 'upload')
|
|
||||||
else:
|
|
||||||
return run_platformio('platformio', 'run', '-d', get_base_path(config),
|
|
||||||
'-t', 'upload', '--upload-port', args.upload_port)
|
|
||||||
|
|
||||||
if port is not None:
|
if port is not None:
|
||||||
_LOGGER.info("Serial device discovered, using it for upload")
|
|
||||||
return run_platformio('platformio', 'run', '-d', get_base_path(config),
|
return run_platformio('platformio', 'run', '-d', get_base_path(config),
|
||||||
'-t', 'upload', '--upload-port', port)
|
'-t', 'upload', '--upload-port', port)
|
||||||
|
|
||||||
@@ -160,7 +174,7 @@ def upload_program(config, args, port):
|
|||||||
|
|
||||||
|
|
||||||
def show_logs(config, args, port):
|
def show_logs(config, args, port):
|
||||||
if port is not None:
|
if port is not None and port != 'OTA':
|
||||||
run_miniterm(config, port)
|
run_miniterm(config, port)
|
||||||
return 0
|
return 0
|
||||||
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)
|
||||||
@@ -205,17 +219,14 @@ def main():
|
|||||||
parser.add_argument('configuration', help='Your YAML configuration file.')
|
parser.add_argument('configuration', help='Your YAML configuration file.')
|
||||||
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
||||||
subparsers.required = True
|
subparsers.required = True
|
||||||
parser_config = subparsers.add_parser('config',
|
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
||||||
help='Validate the configuration and spit it out.')
|
|
||||||
|
|
||||||
parser_compile = subparsers.add_parser('compile',
|
subparsers.add_parser('compile', help='Read the configuration and compile a program.')
|
||||||
help='Read the configuration and compile a program.')
|
|
||||||
|
|
||||||
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
|
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
|
||||||
'and upload the latest binary.')
|
'and upload the latest binary.')
|
||||||
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
||||||
"For example /dev/cu.SLAB_USBtoUAR.",
|
"For example /dev/cu.SLAB_USBtoUART.")
|
||||||
nargs='?', const='HELLO')
|
|
||||||
parser_upload.add_argument('--host-port', help="Specify the host port.", type=int)
|
parser_upload.add_argument('--host-port', help="Specify the host port.", type=int)
|
||||||
|
|
||||||
parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
|
parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
|
||||||
@@ -224,12 +235,13 @@ def main():
|
|||||||
parser_logs.add_argument('--username', help='Manually set the username.')
|
parser_logs.add_argument('--username', help='Manually set the username.')
|
||||||
parser_logs.add_argument('--password', help='Manually set the password.')
|
parser_logs.add_argument('--password', help='Manually set the password.')
|
||||||
parser_logs.add_argument('--client-id', help='Manually set the client id.')
|
parser_logs.add_argument('--client-id', help='Manually set the client id.')
|
||||||
|
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
|
||||||
|
"For example /dev/cu.SLAB_USBtoUART.")
|
||||||
|
|
||||||
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
|
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
|
||||||
'upload it, and start MQTT logs.')
|
'upload it, and start MQTT logs.')
|
||||||
parser_run.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
parser_run.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
||||||
"For example /dev/cu.SLAB_USBtoUAR.",
|
"For example /dev/cu.SLAB_USBtoUART.")
|
||||||
nargs='?', const='HELLO')
|
|
||||||
parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int)
|
parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int)
|
||||||
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
|
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
@@ -245,7 +257,7 @@ def main():
|
|||||||
parser_clean.add_argument('--password', help='Manually set the password.')
|
parser_clean.add_argument('--password', help='Manually set the password.')
|
||||||
parser_clean.add_argument('--client-id', help='Manually set the client id.')
|
parser_clean.add_argument('--client-id', help='Manually set the client id.')
|
||||||
|
|
||||||
parser_wizard = subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
|
subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
|
||||||
"you through setting up esphomeyaml.")
|
"you through setting up esphomeyaml.")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -260,6 +272,7 @@ def main():
|
|||||||
|
|
||||||
if args.command == 'config':
|
if args.command == 'config':
|
||||||
print(yaml_util.dump(config))
|
print(yaml_util.dump(config))
|
||||||
|
return 0
|
||||||
elif args.command == 'compile':
|
elif args.command == 'compile':
|
||||||
exit_code = write_cpp(config)
|
exit_code = write_cpp(config)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
@@ -270,14 +283,14 @@ def main():
|
|||||||
_LOGGER.info(u"Successfully compiled program.")
|
_LOGGER.info(u"Successfully compiled program.")
|
||||||
return 0
|
return 0
|
||||||
elif args.command == 'upload':
|
elif args.command == 'upload':
|
||||||
port = discover_serial_ports()
|
port = args.upload_port or discover_serial_ports()
|
||||||
exit_code = upload_program(config, args, port)
|
exit_code = upload_program(config, args, port)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info(u"Successfully uploaded program.")
|
_LOGGER.info(u"Successfully uploaded program.")
|
||||||
return 0
|
return 0
|
||||||
elif args.command == 'logs':
|
elif args.command == 'logs':
|
||||||
port = discover_serial_ports()
|
port = args.serial_port or discover_serial_ports()
|
||||||
return show_logs(config, args, port)
|
return show_logs(config, args, port)
|
||||||
elif args.command == 'clean-mqtt':
|
elif args.command == 'clean-mqtt':
|
||||||
return clean_mqtt(config, args)
|
return clean_mqtt(config, args)
|
||||||
@@ -290,14 +303,13 @@ def main():
|
|||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info(u"Successfully compiled program.")
|
_LOGGER.info(u"Successfully compiled program.")
|
||||||
if args.no_logs:
|
if args.no_logs:
|
||||||
return
|
return 0
|
||||||
port = discover_serial_ports()
|
port = args.upload_port or discover_serial_ports()
|
||||||
exit_code = upload_program(config, args, port)
|
exit_code = upload_program(config, args, port)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info(u"Successfully uploaded program.")
|
_LOGGER.info(u"Successfully uploaded program.")
|
||||||
return show_logs(config, args, port)
|
return show_logs(config, args, port)
|
||||||
else:
|
|
||||||
print(u"Unknown command {}".format(args.command))
|
print(u"Unknown command {}".format(args.command))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ from esphomeyaml.helpers import App, Pvariable, RawExpression, add, exp_empty_op
|
|||||||
|
|
||||||
LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE']
|
LOG_LEVELS = ['NONE', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'VERBOSE']
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
is_log_level = vol.All(vol.Upper, vol.Any(*LOG_LEVELS))
|
is_log_level = vol.All(vol.Upper, vol.Any(*LOG_LEVELS))
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
|
CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ def to_code(config):
|
|||||||
rhs = App.init_ota()
|
rhs = App.init_ota()
|
||||||
ota = Pvariable('OTAComponent', config[CONF_ID], rhs)
|
ota = Pvariable('OTAComponent', config[CONF_ID], rhs)
|
||||||
if CONF_PASSWORD in config:
|
if CONF_PASSWORD in config:
|
||||||
h = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
|
hash_ = hashlib.md5(config[CONF_PASSWORD].encode()).hexdigest()
|
||||||
add(ota.set_auth_password_hash(h))
|
add(ota.set_auth_password_hash(hash_))
|
||||||
if config[CONF_SAFE_MODE]:
|
if config[CONF_SAFE_MODE]:
|
||||||
add(ota.start_safe_mode())
|
add(ota.start_safe_mode())
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ MQTT_SENSOR_ID_SCHEMA = MQTT_SENSOR_SCHEMA.extend({
|
|||||||
cv.GenerateID('mqtt_sensor'): cv.register_variable_id,
|
cv.GenerateID('mqtt_sensor'): cv.register_variable_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
OffsetFilter = MockObj('new sensor::OffsetFilter')
|
OffsetFilter = MockObj('new sensor::OffsetFilter')
|
||||||
MultiplyFilter = MockObj('new sensor::MultiplyFilter')
|
MultiplyFilter = MockObj('new sensor::MultiplyFilter')
|
||||||
FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter')
|
FilterOutValueFilter = MockObj('new sensor::FilterOutValueFilter')
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ PLATFORM_SCHEMA = switch.PLATFORM_SCHEMA.extend({
|
|||||||
vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id,
|
vol.Optional(CONF_IR_TRANSMITTER_ID): cv.variable_id,
|
||||||
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
|
}).extend(switch.MQTT_SWITCH_SCHEMA.schema)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
SendData = MockObj('switch_::ir::SendData', '::')
|
SendData = MockObj('switch_::ir::SendData', '::')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ CONFIG_SCHEMA = cv.ID_SCHEMA.extend({
|
|||||||
vol.Optional(CONF_HOSTNAME): cv.hostname,
|
vol.Optional(CONF_HOSTNAME): cv.hostname,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
IPAddress = MockObj('IPAddress')
|
IPAddress = MockObj('IPAddress')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from esphomeyaml.helpers import App, add, add_task, color
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_LIBRARY_URI = u'https://github.com/OttoWinter/esphomelib.git#v1.2.1'
|
DEFAULT_LIBRARY_URI = u'esphomelib@1.2.1'
|
||||||
|
|
||||||
CORE_SCHEMA = vol.Schema({
|
CORE_SCHEMA = vol.Schema({
|
||||||
vol.Required(CONF_NAME): cv.valid_name,
|
vol.Required(CONF_NAME): cv.valid_name,
|
||||||
@@ -50,7 +50,6 @@ def get_component(domain):
|
|||||||
module = importlib.import_module(path)
|
module = importlib.import_module(path)
|
||||||
except ImportError as err:
|
except ImportError as err:
|
||||||
_LOGGER.debug(err)
|
_LOGGER.debug(err)
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
_COMPONENT_CACHE[domain] = module
|
_COMPONENT_CACHE[domain] = module
|
||||||
return module
|
return module
|
||||||
@@ -87,7 +86,7 @@ def validate_config(config):
|
|||||||
|
|
||||||
for req in REQUIRED_COMPONENTS:
|
for req in REQUIRED_COMPONENTS:
|
||||||
if req not in config:
|
if req not in config:
|
||||||
raise ESPHomeYAMLError("Component %s is required for esphomeyaml.", req)
|
raise ESPHomeYAMLError("Component {} is required for esphomeyaml.".format(req))
|
||||||
|
|
||||||
_ALL_COMPONENTS = list(config.keys())
|
_ALL_COMPONENTS = list(config.keys())
|
||||||
|
|
||||||
@@ -137,7 +136,7 @@ def validate_config(config):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
platforms = []
|
platforms = []
|
||||||
for i, p_config in enumerate(conf):
|
for p_config in conf:
|
||||||
if not isinstance(p_config, dict):
|
if not isinstance(p_config, dict):
|
||||||
result.add_error(u"Platform schemas mus have 'platform:' key")
|
result.add_error(u"Platform schemas mus have 'platform:' key")
|
||||||
continue
|
continue
|
||||||
@@ -197,7 +196,7 @@ def load_config(path):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
result = validate_config(config)
|
result = validate_config(config)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
print(u"Unexpected exception while reading configuration:")
|
print(u"Unexpected exception while reading configuration:")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -262,7 +261,11 @@ def dump_dict(layer, indent_count=3, listi=False, **kwargs):
|
|||||||
|
|
||||||
def read_config(path):
|
def read_config(path):
|
||||||
_LOGGER.debug("Reading configuration...")
|
_LOGGER.debug("Reading configuration...")
|
||||||
|
try:
|
||||||
res = load_config(path)
|
res = load_config(path)
|
||||||
|
except ESPHomeYAMLError as e:
|
||||||
|
_LOGGER.error(u"Error while reading config: %s", e)
|
||||||
|
return None
|
||||||
excepts = {}
|
excepts = {}
|
||||||
for err in res.errors:
|
for err in res.errors:
|
||||||
domain = err[1] or u"General Error"
|
domain = err[1] or u"General Error"
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ from esphomeyaml.helpers import ensure_unique_string
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
|
||||||
port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
|
port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
|
||||||
positive_float = vol.All(vol.Coerce(float), vol.Range(min=0))
|
positive_float = vol.All(vol.Coerce(float), vol.Range(min=0))
|
||||||
zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1)),
|
zero_to_one_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
|
||||||
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
|
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
|
||||||
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
|
positive_not_null_int = vol.All(vol.Coerce(int), vol.Range(min=0, min_included=False))
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ def alphanumeric(value):
|
|||||||
def valid_name(value):
|
def valid_name(value):
|
||||||
value = string_strict(value)
|
value = string_strict(value)
|
||||||
if not all(c in ALLOWED_NAME_CHARS for c in value):
|
if not all(c in ALLOWED_NAME_CHARS for c in value):
|
||||||
raise vol.Invalid(u"Valid characters for name are %s", ALLOWED_NAME_CHARS)
|
raise vol.Invalid(u"Valid characters for name are {}".format(ALLOWED_NAME_CHARS))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ def string(value):
|
|||||||
|
|
||||||
def string_strict(value):
|
def string_strict(value):
|
||||||
"""Strictly only allow strings."""
|
"""Strictly only allow strings."""
|
||||||
if isinstance(value, str) or isinstance(value, unicode):
|
if isinstance(value, (str, unicode)):
|
||||||
return value
|
return value
|
||||||
raise vol.Invalid("Must be string, did you forget putting quotes "
|
raise vol.Invalid("Must be string, did you forget putting quotes "
|
||||||
"around the value?")
|
"around the value?")
|
||||||
@@ -148,7 +150,6 @@ def only_on(platforms):
|
|||||||
platforms = [platforms]
|
platforms = [platforms]
|
||||||
|
|
||||||
def validator_(obj):
|
def validator_(obj):
|
||||||
print(obj)
|
|
||||||
if ESP_PLATFORM not in platforms:
|
if ESP_PLATFORM not in platforms:
|
||||||
raise vol.Invalid(u"This feature is only available on {}".format(platforms))
|
raise vol.Invalid(u"This feature is only available on {}".format(platforms))
|
||||||
return obj
|
return obj
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
MAJOR_VERSION = 1
|
MAJOR_VERSION = 1
|
||||||
MINOR_VERSION = 2
|
MINOR_VERSION = 2
|
||||||
PATCH_VERSION = '1'
|
PATCH_VERSION = '2'
|
||||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ import random
|
|||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
|
|
||||||
# Commands
|
# Commands
|
||||||
FLASH = 0
|
FLASH = 0
|
||||||
SPIFFS = 100
|
SPIFFS = 100
|
||||||
@@ -62,7 +64,7 @@ def update_progress(progress):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if PROGRESS:
|
if PROGRESS:
|
||||||
barLength = 60 # Modify this to change the length of the progress bar
|
bar_length = 60 # Modify this to change the length of the progress bar
|
||||||
status = ""
|
status = ""
|
||||||
if isinstance(progress, int):
|
if isinstance(progress, int):
|
||||||
progress = float(progress)
|
progress = float(progress)
|
||||||
@@ -75,8 +77,8 @@ def update_progress(progress):
|
|||||||
if progress >= 1:
|
if progress >= 1:
|
||||||
progress = 1
|
progress = 1
|
||||||
status = "Done...\r\n"
|
status = "Done...\r\n"
|
||||||
block = int(round(barLength * progress))
|
block = int(round(bar_length * progress))
|
||||||
text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (barLength - block),
|
text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (bar_length - block),
|
||||||
int(progress * 100), status)
|
int(progress * 100), status)
|
||||||
sys.stderr.write(text)
|
sys.stderr.write(text)
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
@@ -93,14 +95,14 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
|||||||
try:
|
try:
|
||||||
sock.bind(server_address)
|
sock.bind(server_address)
|
||||||
sock.listen(1)
|
sock.listen(1)
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.error("Listen Failed")
|
_LOGGER.error("Listen Failed")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
content_size = os.path.getsize(filename)
|
content_size = os.path.getsize(filename)
|
||||||
f = open(filename, 'rb')
|
f_handle = open(filename, 'rb')
|
||||||
file_md5 = hashlib.md5(f.read()).hexdigest()
|
file_md5 = hashlib.md5(f_handle.read()).hexdigest()
|
||||||
f.close()
|
f_handle.close()
|
||||||
_LOGGER.info('Upload size: %d', content_size)
|
_LOGGER.info('Upload size: %d', content_size)
|
||||||
message = '%d %d %d %s\n' % (command, local_port, content_size, file_md5)
|
message = '%d %d %d %s\n' % (command, local_port, content_size, file_md5)
|
||||||
|
|
||||||
@@ -116,7 +118,7 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
|||||||
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
try:
|
try:
|
||||||
sock2.sendto(message.encode(), remote_address)
|
sock2.sendto(message.encode(), remote_address)
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.error('Failed')
|
_LOGGER.error('Failed')
|
||||||
sock2.close()
|
sock2.close()
|
||||||
_LOGGER.error('Host %s Not Found', remote_host)
|
_LOGGER.error('Host %s Not Found', remote_host)
|
||||||
@@ -125,7 +127,7 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
|||||||
try:
|
try:
|
||||||
data = sock2.recv(37).decode()
|
data = sock2.recv(37).decode()
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
sys.stderr.write('.')
|
sys.stderr.write('.')
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
sock2.close()
|
sock2.close()
|
||||||
@@ -148,7 +150,7 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
|||||||
sock2.settimeout(10)
|
sock2.settimeout(10)
|
||||||
try:
|
try:
|
||||||
data = sock2.recv(32).decode()
|
data = sock2.recv(32).decode()
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.error('FAIL: No Answer to our Authentication')
|
_LOGGER.error('FAIL: No Answer to our Authentication')
|
||||||
sock2.close()
|
sock2.close()
|
||||||
return 1
|
return 1
|
||||||
@@ -166,35 +168,36 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
|||||||
_LOGGER.info('Waiting for device...')
|
_LOGGER.info('Waiting for device...')
|
||||||
try:
|
try:
|
||||||
sock.settimeout(10)
|
sock.settimeout(10)
|
||||||
connection, client_address = sock.accept()
|
connection, _ = sock.accept()
|
||||||
sock.settimeout(None)
|
sock.settimeout(None)
|
||||||
connection.settimeout(None)
|
connection.settimeout(None)
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.error('No response from device')
|
_LOGGER.error('No response from device')
|
||||||
sock.close()
|
sock.close()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = open(filename, "rb")
|
f_handle = open(filename, "rb")
|
||||||
if PROGRESS:
|
if PROGRESS:
|
||||||
update_progress(0)
|
update_progress(0)
|
||||||
else:
|
else:
|
||||||
_LOGGER.info('Uploading...')
|
_LOGGER.info('Uploading...')
|
||||||
offset = 0
|
offset = 0
|
||||||
while True:
|
while True:
|
||||||
chunk = f.read(1024)
|
chunk = f_handle.read(1024)
|
||||||
if not chunk: break
|
if not chunk:
|
||||||
|
break
|
||||||
offset += len(chunk)
|
offset += len(chunk)
|
||||||
update_progress(offset / float(content_size))
|
update_progress(offset / float(content_size))
|
||||||
connection.settimeout(10)
|
connection.settimeout(10)
|
||||||
try:
|
try:
|
||||||
connection.sendall(chunk)
|
connection.sendall(chunk)
|
||||||
connection.recv(10)
|
connection.recv(10)
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
sys.stderr.write('\n')
|
sys.stderr.write('\n')
|
||||||
_LOGGER.error('Error Uploading')
|
_LOGGER.error('Error Uploading')
|
||||||
connection.close()
|
connection.close()
|
||||||
f.close()
|
f_handle.close()
|
||||||
sock.close()
|
sock.close()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -207,26 +210,26 @@ def serve(remote_host, local_addr, remote_port, local_port, password, filename,
|
|||||||
break
|
break
|
||||||
_LOGGER.info('Result: OK')
|
_LOGGER.info('Result: OK')
|
||||||
connection.close()
|
connection.close()
|
||||||
f.close()
|
f_handle.close()
|
||||||
sock.close()
|
sock.close()
|
||||||
if data != "OK":
|
if data != "OK":
|
||||||
_LOGGER.error('%s', data)
|
_LOGGER.error('%s', data)
|
||||||
return 1
|
return 1
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.error('No Result!')
|
_LOGGER.error('No Result!')
|
||||||
connection.close()
|
connection.close()
|
||||||
f.close()
|
f_handle.close()
|
||||||
sock.close()
|
sock.close()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
connection.close()
|
connection.close()
|
||||||
f.close()
|
f_handle.close()
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def parser(unparsed_args):
|
def parse_args(unparsed_args):
|
||||||
parser = optparse.OptionParser(
|
parser = optparse.OptionParser(
|
||||||
usage="%prog [options]",
|
usage="%prog [options]",
|
||||||
description="Transmit image over the air to the esp8266 module with OTA support."
|
description="Transmit image over the air to the esp8266 module with OTA support."
|
||||||
@@ -234,25 +237,29 @@ def parser(unparsed_args):
|
|||||||
|
|
||||||
# destination ip and port
|
# destination ip and port
|
||||||
group = optparse.OptionGroup(parser, "Destination")
|
group = optparse.OptionGroup(parser, "Destination")
|
||||||
group.add_option("-i", "--ip",
|
group.add_option(
|
||||||
|
"-i", "--ip",
|
||||||
dest="esp_ip",
|
dest="esp_ip",
|
||||||
action="store",
|
action="store",
|
||||||
help="ESP8266 IP Address.",
|
help="ESP8266 IP Address.",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
group.add_option("-I", "--host_ip",
|
group.add_option(
|
||||||
|
"-I", "--host_ip",
|
||||||
dest="host_ip",
|
dest="host_ip",
|
||||||
action="store",
|
action="store",
|
||||||
help="Host IP Address.",
|
help="Host IP Address.",
|
||||||
default="0.0.0.0"
|
default="0.0.0.0"
|
||||||
)
|
)
|
||||||
group.add_option("-p", "--port",
|
group.add_option(
|
||||||
|
"-p", "--port",
|
||||||
dest="esp_port",
|
dest="esp_port",
|
||||||
type="int",
|
type="int",
|
||||||
help="ESP8266 ota Port. Default 8266",
|
help="ESP8266 ota Port. Default 8266",
|
||||||
default=8266
|
default=8266
|
||||||
)
|
)
|
||||||
group.add_option("-P", "--host_port",
|
group.add_option(
|
||||||
|
"-P", "--host_port",
|
||||||
dest="host_port",
|
dest="host_port",
|
||||||
type="int",
|
type="int",
|
||||||
help="Host server ota Port. Default random 10000-60000",
|
help="Host server ota Port. Default random 10000-60000",
|
||||||
@@ -262,7 +269,8 @@ def parser(unparsed_args):
|
|||||||
|
|
||||||
# auth
|
# auth
|
||||||
group = optparse.OptionGroup(parser, "Authentication")
|
group = optparse.OptionGroup(parser, "Authentication")
|
||||||
group.add_option("-a", "--auth",
|
group.add_option(
|
||||||
|
"-a", "--auth",
|
||||||
dest="auth",
|
dest="auth",
|
||||||
help="Set authentication password.",
|
help="Set authentication password.",
|
||||||
action="store",
|
action="store",
|
||||||
@@ -272,13 +280,15 @@ def parser(unparsed_args):
|
|||||||
|
|
||||||
# image
|
# image
|
||||||
group = optparse.OptionGroup(parser, "Image")
|
group = optparse.OptionGroup(parser, "Image")
|
||||||
group.add_option("-f", "--file",
|
group.add_option(
|
||||||
|
"-f", "--file",
|
||||||
dest="image",
|
dest="image",
|
||||||
help="Image file.",
|
help="Image file.",
|
||||||
metavar="FILE",
|
metavar="FILE",
|
||||||
default=None
|
default=None
|
||||||
)
|
)
|
||||||
group.add_option("-s", "--spiffs",
|
group.add_option(
|
||||||
|
"-s", "--spiffs",
|
||||||
dest="spiffs",
|
dest="spiffs",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Use this option to transmit a SPIFFS image and do not flash the "
|
help="Use this option to transmit a SPIFFS image and do not flash the "
|
||||||
@@ -289,13 +299,15 @@ def parser(unparsed_args):
|
|||||||
|
|
||||||
# output group
|
# output group
|
||||||
group = optparse.OptionGroup(parser, "Output")
|
group = optparse.OptionGroup(parser, "Output")
|
||||||
group.add_option("-d", "--debug",
|
group.add_option(
|
||||||
|
"-d", "--debug",
|
||||||
dest="debug",
|
dest="debug",
|
||||||
help="Show debug output. And override loglevel with debug.",
|
help="Show debug output. And override loglevel with debug.",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
group.add_option("-r", "--progress",
|
group.add_option(
|
||||||
|
"-r", "--progress",
|
||||||
dest="progress",
|
dest="progress",
|
||||||
help="Show progress output. Does not work for ArduinoIDE",
|
help="Show progress output. Does not work for ArduinoIDE",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@@ -303,13 +315,13 @@ def parser(unparsed_args):
|
|||||||
)
|
)
|
||||||
parser.add_option_group(group)
|
parser.add_option_group(group)
|
||||||
|
|
||||||
(options, args) = parser.parse_args(unparsed_args)
|
options, _ = parser.parse_args(unparsed_args)
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
options = parser(args)
|
options = parse_args(args)
|
||||||
_LOGGER.debug("Options: %s", str(options))
|
_LOGGER.debug("Options: %s", str(options))
|
||||||
|
|
||||||
# check options
|
# check options
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class Expression(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
raise NotImplemented
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class RawExpression(Expression):
|
class RawExpression(Expression):
|
||||||
@@ -107,11 +107,11 @@ class StructInitializer(Expression):
|
|||||||
self.args[key] = safe_exp(value)
|
self.args[key] = safe_exp(value)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = u'{}{{\n'.format(self.base)
|
cpp = u'{}{{\n'.format(self.base)
|
||||||
for key, value in self.args.iteritems():
|
for key, value in self.args.iteritems():
|
||||||
s += u' .{} = {},\n'.format(key, value)
|
cpp += u' .{} = {},\n'.format(key, value)
|
||||||
s += u'}'
|
cpp += u'}'
|
||||||
return s
|
return cpp
|
||||||
|
|
||||||
|
|
||||||
class ArrayInitializer(Expression):
|
class ArrayInitializer(Expression):
|
||||||
@@ -122,25 +122,25 @@ class ArrayInitializer(Expression):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
if not self.args:
|
if not self.args:
|
||||||
return u'{}'
|
return u'{}'
|
||||||
s = u'{\n'
|
cpp = u'{\n'
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
s += u' {},\n'.format(arg)
|
cpp += u' {},\n'.format(arg)
|
||||||
s += u'}'
|
cpp += u'}'
|
||||||
return s
|
return cpp
|
||||||
|
|
||||||
|
|
||||||
class Literal(Expression):
|
class Literal(Expression):
|
||||||
def __init__(self):
|
def __str__(self):
|
||||||
super(Literal, self).__init__()
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class StringLiteral(Literal):
|
class StringLiteral(Literal):
|
||||||
def __init__(self, s):
|
def __init__(self, string):
|
||||||
super(StringLiteral, self).__init__()
|
super(StringLiteral, self).__init__()
|
||||||
self.s = s
|
self.string = string
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u'"{}"'.format(self.s)
|
return u'"{}"'.format(self.string)
|
||||||
|
|
||||||
|
|
||||||
class IntLiteral(Literal):
|
class IntLiteral(Literal):
|
||||||
@@ -153,12 +153,12 @@ class IntLiteral(Literal):
|
|||||||
|
|
||||||
|
|
||||||
class BoolLiteral(Literal):
|
class BoolLiteral(Literal):
|
||||||
def __init__(self, b):
|
def __init__(self, binary):
|
||||||
super(BoolLiteral, self).__init__()
|
super(BoolLiteral, self).__init__()
|
||||||
self.b = b
|
self.binary = binary
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"true" if self.b else u"false"
|
return u"true" if self.binary else u"false"
|
||||||
|
|
||||||
|
|
||||||
class HexIntLiteral(Literal):
|
class HexIntLiteral(Literal):
|
||||||
@@ -171,12 +171,12 @@ class HexIntLiteral(Literal):
|
|||||||
|
|
||||||
|
|
||||||
class FloatLiteral(Literal):
|
class FloatLiteral(Literal):
|
||||||
def __init__(self, f):
|
def __init__(self, float_):
|
||||||
super(FloatLiteral, self).__init__()
|
super(FloatLiteral, self).__init__()
|
||||||
self.f = f
|
self.float_ = float_
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"{:f}f".format(self.f)
|
return u"{:f}f".format(self.float_)
|
||||||
|
|
||||||
|
|
||||||
def safe_exp(obj):
|
def safe_exp(obj):
|
||||||
@@ -184,7 +184,7 @@ def safe_exp(obj):
|
|||||||
return obj
|
return obj
|
||||||
elif isinstance(obj, bool):
|
elif isinstance(obj, bool):
|
||||||
return BoolLiteral(obj)
|
return BoolLiteral(obj)
|
||||||
elif isinstance(obj, str) or isinstance(obj, unicode):
|
elif isinstance(obj, (str, unicode)):
|
||||||
return StringLiteral(obj)
|
return StringLiteral(obj)
|
||||||
elif isinstance(obj, (int, long)):
|
elif isinstance(obj, (int, long)):
|
||||||
return IntLiteral(obj)
|
return IntLiteral(obj)
|
||||||
@@ -198,7 +198,7 @@ class Statement(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
raise NotImplemented
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class RawStatement(Statement):
|
class RawStatement(Statement):
|
||||||
@@ -225,6 +225,7 @@ def statement(expression):
|
|||||||
return ExpressionStatement(expression)
|
return ExpressionStatement(expression)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=redefined-builtin, invalid-name
|
||||||
def variable(type, id, rhs):
|
def variable(type, id, rhs):
|
||||||
lhs = RawExpression(u'{} {}'.format(type if not SIMPLIFY else u'auto', id))
|
lhs = RawExpression(u'{} {}'.format(type if not SIMPLIFY else u'auto', id))
|
||||||
rhs = safe_exp(rhs)
|
rhs = safe_exp(rhs)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def initialize(config, subscriptions, on_message, username, password, client_id):
|
def initialize(config, subscriptions, on_message, username, password, client_id):
|
||||||
def on_connect(client, userdata, flags, rc):
|
def on_connect(client, userdata, flags, return_code):
|
||||||
for topic in subscriptions:
|
for topic in subscriptions:
|
||||||
client.subscribe(topic)
|
client.subscribe(topic)
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None):
|
|||||||
_LOGGER.info(u"Starting log output from %s", topic)
|
_LOGGER.info(u"Starting log output from %s", topic)
|
||||||
|
|
||||||
def on_message(client, userdata, msg):
|
def on_message(client, userdata, msg):
|
||||||
t = datetime.now().time().strftime(u'[%H:%M:%S] ')
|
time = datetime.now().time().strftime(u'[%H:%M:%S] ')
|
||||||
print(t + msg.payload)
|
print(time + msg.payload)
|
||||||
|
|
||||||
return initialize(config, [topic], on_message, username, password, client_id)
|
return initialize(config, [topic], on_message, username, password, client_id)
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
|
|||||||
discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant')
|
discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant')
|
||||||
name = config[CONF_ESPHOMEYAML][CONF_NAME]
|
name = config[CONF_ESPHOMEYAML][CONF_NAME]
|
||||||
topic = u'{}/+/{}/#'.format(discovery_prefix, name)
|
topic = u'{}/+/{}/#'.format(discovery_prefix, name)
|
||||||
_LOGGER.info(u"Clearing messages from {}".format(topic))
|
_LOGGER.info(u"Clearing messages from %s", topic)
|
||||||
|
|
||||||
def on_message(client, userdata, msg):
|
def on_message(client, userdata, msg):
|
||||||
if not msg.payload:
|
if not msg.payload:
|
||||||
|
|||||||
@@ -101,16 +101,16 @@ def _validate_gpio_pin(value):
|
|||||||
if value < 0 or value > 39:
|
if value < 0 or value > 39:
|
||||||
raise vol.Invalid(u"ESP32: Invalid pin number: {}".format(value))
|
raise vol.Invalid(u"ESP32: Invalid pin number: {}".format(value))
|
||||||
if 6 <= value <= 11:
|
if 6 <= value <= 11:
|
||||||
_LOGGER.warning(u"ESP32: Pin {} (6-11) might already be used by the "
|
_LOGGER.warning(u"ESP32: Pin %s (6-11) might already be used by the "
|
||||||
u"flash interface. Be warned.".format(value))
|
u"flash interface. Be warned.", value)
|
||||||
if value in (20, 24, 28, 29, 30, 31):
|
if value in (20, 24, 28, 29, 30, 31):
|
||||||
_LOGGER.warning(u"ESP32: Pin {} (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.".format(value))
|
u"Be warned.", value)
|
||||||
return value
|
return value
|
||||||
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
elif cv.ESP_PLATFORM == ESP_PLATFORM_ESP8266:
|
||||||
if 6 <= value <= 11:
|
if 6 <= value <= 11:
|
||||||
_LOGGER.warning(u"ESP8266: Pin {} (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.".format(value))
|
u"flash interface. Be warned.", value)
|
||||||
if value < 0 or value > 17:
|
if value < 0 or value > 17:
|
||||||
raise vol.Invalid(u"ESP8266: Invalid pin number: {}".format(value))
|
raise vol.Invalid(u"ESP8266: Invalid pin number: {}".format(value))
|
||||||
return value
|
return value
|
||||||
@@ -153,6 +153,7 @@ def analog_pin(value):
|
|||||||
raise vol.Invalid(u"Invalid ESP platform.")
|
raise vol.Invalid(u"Invalid ESP platform.")
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
input_output_pin = vol.All(input_pin, output_pin)
|
input_output_pin = vol.All(input_pin, output_pin)
|
||||||
gpio_pin = vol.Any(input_pin, output_pin)
|
gpio_pin = vol.Any(input_pin, output_pin)
|
||||||
PIN_MODES_ESP8266 = [
|
PIN_MODES_ESP8266 = [
|
||||||
|
|||||||
@@ -2,16 +2,18 @@ from __future__ import print_function
|
|||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
import os
|
import os
|
||||||
from time import sleep
|
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import esphomeyaml.config_validation as cv
|
import esphomeyaml.config_validation as cv
|
||||||
from esphomeyaml.components import mqtt
|
from esphomeyaml.components import mqtt
|
||||||
from esphomeyaml.const import ESP_PLATFORMS, ESP_PLATFORM_ESP32, ESP_BOARDS_FOR_PLATFORM
|
from esphomeyaml.const import ESP_BOARDS_FOR_PLATFORM, ESP_PLATFORMS, ESP_PLATFORM_ESP32
|
||||||
from esphomeyaml.helpers import color
|
from esphomeyaml.helpers import color
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=anomalous-backslash-in-string
|
||||||
CORE_BIG = """ _____ ____ _____ ______
|
CORE_BIG = """ _____ ____ _____ ______
|
||||||
/ ____/ __ \| __ \| ____|
|
/ ____/ __ \| __ \| ____|
|
||||||
| | | | | | |__) | |__
|
| | | | | | |__) | |__
|
||||||
@@ -20,7 +22,7 @@ CORE_BIG = """ _____ ____ _____ ______
|
|||||||
\_____\____/|_| \_\______|
|
\_____\____/|_| \_\______|
|
||||||
"""
|
"""
|
||||||
ESP_BIG = """ ______ _____ _____
|
ESP_BIG = """ ______ _____ _____
|
||||||
| ____|/ ____| __ \
|
| ____|/ ____| __ \\
|
||||||
| |__ | (___ | |__) |
|
| |__ | (___ | |__) |
|
||||||
| __| \___ \| ___/
|
| __| \___ \| ___/
|
||||||
| |____ ____) | |
|
| |____ ____) | |
|
||||||
@@ -41,10 +43,10 @@ MQTT_BIG = """ __ __ ____ _______ _______
|
|||||||
|_| |_|\___\_\ |_| |_|
|
|_| |_|\___\_\ |_| |_|
|
||||||
"""
|
"""
|
||||||
OTA_BIG = """ ____ _______
|
OTA_BIG = """ ____ _______
|
||||||
/ __ \__ __|/\
|
/ __ \__ __|/\\
|
||||||
| | | | | | / \
|
| | | | | | / \\
|
||||||
| | | | | | / /\ \
|
| | | | | | / /\ \\
|
||||||
| |__| | | |/ ____ \
|
| |__| | | |/ ____ \\
|
||||||
\____/ |_/_/ \_\\
|
\____/ |_/_/ \_\\
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -85,8 +87,8 @@ def default_input(text, default):
|
|||||||
|
|
||||||
|
|
||||||
# From https://stackoverflow.com/a/518232/8924614
|
# From https://stackoverflow.com/a/518232/8924614
|
||||||
def strip_accents(s):
|
def strip_accents(string):
|
||||||
return u''.join(c for c in unicodedata.normalize('NFD', unicode(s))
|
return u''.join(c for c in unicodedata.normalize('NFD', unicode(string))
|
||||||
if unicodedata.category(c) != 'Mn')
|
if unicodedata.category(c) != 'Mn')
|
||||||
|
|
||||||
|
|
||||||
@@ -230,9 +232,9 @@ def wizard(path):
|
|||||||
try:
|
try:
|
||||||
broker = mqtt.validate_broker(broker)
|
broker = mqtt.validate_broker(broker)
|
||||||
break
|
break
|
||||||
except vol.Invalid as e:
|
except vol.Invalid as err:
|
||||||
print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format(
|
print(color('red', "The broker address \"{}\" seems to be invalid: {} :(".format(
|
||||||
broker, e)))
|
broker, err)))
|
||||||
print("Please try again.")
|
print("Please try again.")
|
||||||
print()
|
print()
|
||||||
sleep(1)
|
sleep(1)
|
||||||
@@ -271,8 +273,8 @@ def wizard(path):
|
|||||||
else:
|
else:
|
||||||
config += "ota:\n"
|
config += "ota:\n"
|
||||||
|
|
||||||
with codecs.open(path, 'w') as f:
|
with codecs.open(path, 'w') as f_handle:
|
||||||
f.write(config)
|
f_handle.write(config)
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print(color('cyan', "DONE! I've now written a new configuration file to ") +
|
print(color('cyan', "DONE! I've now written a new configuration file to ") +
|
||||||
@@ -290,4 +292,3 @@ def wizard(path):
|
|||||||
print(" > Then follow the rest of the getting started guide:")
|
print(" > Then follow the rest of the getting started guide:")
|
||||||
print(" > https://esphomelib.com/esphomeyaml/getting-started.html")
|
print(" > https://esphomelib.com/esphomeyaml/getting-started.html")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import os
|
|||||||
|
|
||||||
from esphomeyaml.config import get_component
|
from esphomeyaml.config import get_component
|
||||||
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_LOGGER, \
|
from esphomeyaml.const import CONF_BOARD, CONF_ESPHOMEYAML, CONF_LIBRARY_URI, CONF_LOGGER, \
|
||||||
CONF_NAME, CONF_OTA, CONF_PLATFORM, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
CONF_NAME, CONF_PLATFORM, ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266
|
||||||
from esphomeyaml.core import ESPHomeYAMLError
|
from esphomeyaml.core import ESPHomeYAMLError
|
||||||
|
|
||||||
CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ==========='
|
CPP_AUTO_GENERATE_BEGIN = u'// ========== AUTO GENERATED CODE BEGIN ==========='
|
||||||
@@ -63,7 +63,7 @@ PLATFORM_TO_PLATFORMIO = {
|
|||||||
|
|
||||||
|
|
||||||
def get_ini_content(config):
|
def get_ini_content(config):
|
||||||
d = {
|
options = {
|
||||||
u'env': config[CONF_ESPHOMEYAML][CONF_NAME],
|
u'env': config[CONF_ESPHOMEYAML][CONF_NAME],
|
||||||
u'platform': PLATFORM_TO_PLATFORMIO[config[CONF_ESPHOMEYAML][CONF_PLATFORM]],
|
u'platform': PLATFORM_TO_PLATFORMIO[config[CONF_ESPHOMEYAML][CONF_PLATFORM]],
|
||||||
u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
|
u'board': config[CONF_ESPHOMEYAML][CONF_BOARD],
|
||||||
@@ -73,8 +73,8 @@ def get_ini_content(config):
|
|||||||
if CONF_LOGGER in config:
|
if CONF_LOGGER in config:
|
||||||
build_flags = get_component(CONF_LOGGER).get_build_flags(config[CONF_LOGGER])
|
build_flags = get_component(CONF_LOGGER).get_build_flags(config[CONF_LOGGER])
|
||||||
if build_flags:
|
if build_flags:
|
||||||
d[u'build_flags'] = u'\n ' + build_flags
|
options[u'build_flags'] = u'\n ' + build_flags
|
||||||
return INI_CONTENT_FORMAT.format(**d)
|
return INI_CONTENT_FORMAT.format(**options)
|
||||||
|
|
||||||
|
|
||||||
def mkdir_p(path):
|
def mkdir_p(path):
|
||||||
@@ -109,8 +109,8 @@ def find_begin_end(text, begin_s, end_s):
|
|||||||
def write_platformio_ini(content, path):
|
def write_platformio_ini(content, path):
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
try:
|
try:
|
||||||
with codecs.open(path, 'r', encoding='utf-8') as f:
|
with codecs.open(path, 'r', encoding='utf-8') as f_handle:
|
||||||
text = f.read()
|
text = f_handle.read()
|
||||||
except OSError:
|
except OSError:
|
||||||
raise ESPHomeYAMLError(u"Could not read ini file at {}".format(path))
|
raise ESPHomeYAMLError(u"Could not read ini file at {}".format(path))
|
||||||
prev_file = text
|
prev_file = text
|
||||||
@@ -123,15 +123,15 @@ def write_platformio_ini(content, path):
|
|||||||
content + INI_AUTO_GENERATE_END + content_format[1]
|
content + INI_AUTO_GENERATE_END + content_format[1]
|
||||||
if prev_file == full_file:
|
if prev_file == full_file:
|
||||||
return
|
return
|
||||||
with codecs.open(path, mode='w+', encoding='utf-8') as f:
|
with codecs.open(path, mode='w+', encoding='utf-8') as f_handle:
|
||||||
f.write(full_file)
|
f_handle.write(full_file)
|
||||||
|
|
||||||
|
|
||||||
def write_cpp(code_s, path):
|
def write_cpp(code_s, path):
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
try:
|
try:
|
||||||
with codecs.open(path, 'r', encoding='utf-8') as f:
|
with codecs.open(path, 'r', encoding='utf-8') as f_handle:
|
||||||
text = f.read()
|
text = f_handle.read()
|
||||||
except OSError:
|
except OSError:
|
||||||
raise ESPHomeYAMLError(u"Could not read C++ file at {}".format(path))
|
raise ESPHomeYAMLError(u"Could not read C++ file at {}".format(path))
|
||||||
prev_file = text
|
prev_file = text
|
||||||
@@ -145,5 +145,5 @@ def write_cpp(code_s, path):
|
|||||||
code_s + CPP_AUTO_GENERATE_END + code_format[1]
|
code_s + CPP_AUTO_GENERATE_END + code_format[1]
|
||||||
if prev_file == full_file:
|
if prev_file == full_file:
|
||||||
return
|
return
|
||||||
with codecs.open(path, 'w+', encoding='utf-8') as f:
|
with codecs.open(path, 'w+', encoding='utf-8') as f_handle:
|
||||||
f.write(full_file)
|
f_handle.write(full_file)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import codecs
|
import codecs
|
||||||
|
import fnmatch
|
||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
import os
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@@ -9,6 +11,11 @@ from esphomeyaml.core import ESPHomeYAMLError, HexInt, IPAddress
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Mostly copied from Home Assistant because that code works fine and
|
||||||
|
# let's not reinvent the wheel here
|
||||||
|
|
||||||
|
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."""
|
||||||
@@ -97,8 +104,107 @@ def _add_reference(obj, loader, node):
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def _env_var_yaml(loader, node):
|
||||||
|
"""Load environment variables and embed it into the configuration YAML."""
|
||||||
|
args = node.value.split()
|
||||||
|
|
||||||
|
# Check for a default value
|
||||||
|
if len(args) > 1:
|
||||||
|
return os.getenv(args[0], u' '.join(args[1:]))
|
||||||
|
elif args[0] in os.environ:
|
||||||
|
return os.environ[args[0]]
|
||||||
|
raise ESPHomeYAMLError(u"Environment variable {} not defined.".format(node.value))
|
||||||
|
|
||||||
|
|
||||||
|
def _include_yaml(loader, node):
|
||||||
|
"""Load another YAML file and embeds it using the !include tag.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
device_tracker: !include device_tracker.yaml
|
||||||
|
"""
|
||||||
|
fname = os.path.join(os.path.dirname(loader.name), node.value)
|
||||||
|
return _add_reference(load_yaml(fname), loader, node)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_file_valid(name):
|
||||||
|
"""Decide if a file is valid."""
|
||||||
|
return not name.startswith(u'.')
|
||||||
|
|
||||||
|
|
||||||
|
def _find_files(directory, pattern):
|
||||||
|
"""Recursively load files in a directory."""
|
||||||
|
for root, dirs, files in os.walk(directory, topdown=True):
|
||||||
|
dirs[:] = [d for d in dirs if _is_file_valid(d)]
|
||||||
|
for basename in files:
|
||||||
|
if _is_file_valid(basename) and fnmatch.fnmatch(basename, pattern):
|
||||||
|
filename = os.path.join(root, basename)
|
||||||
|
yield filename
|
||||||
|
|
||||||
|
|
||||||
|
def _include_dir_named_yaml(loader, node):
|
||||||
|
"""Load multiple files from directory as a dictionary."""
|
||||||
|
mapping = OrderedDict() # type: OrderedDict
|
||||||
|
loc = os.path.join(os.path.dirname(loader.name), node.value)
|
||||||
|
for fname in _find_files(loc, '*.yaml'):
|
||||||
|
filename = os.path.splitext(os.path.basename(fname))[0]
|
||||||
|
mapping[filename] = load_yaml(fname)
|
||||||
|
return _add_reference(mapping, loader, node)
|
||||||
|
|
||||||
|
|
||||||
|
def _include_dir_merge_named_yaml(loader, node):
|
||||||
|
"""Load multiple files from directory as a merged dictionary."""
|
||||||
|
mapping = OrderedDict() # type: OrderedDict
|
||||||
|
loc = os.path.join(os.path.dirname(loader.name), node.value)
|
||||||
|
for fname in _find_files(loc, '*.yaml'):
|
||||||
|
if os.path.basename(fname) == SECRET_YAML:
|
||||||
|
continue
|
||||||
|
loaded_yaml = load_yaml(fname)
|
||||||
|
if isinstance(loaded_yaml, dict):
|
||||||
|
mapping.update(loaded_yaml)
|
||||||
|
return _add_reference(mapping, loader, node)
|
||||||
|
|
||||||
|
|
||||||
|
def _include_dir_list_yaml(loader, node):
|
||||||
|
"""Load multiple files from directory as a list."""
|
||||||
|
loc = os.path.join(os.path.dirname(loader.name), node.value)
|
||||||
|
return [load_yaml(f) for f in _find_files(loc, '*.yaml')
|
||||||
|
if os.path.basename(f) != SECRET_YAML]
|
||||||
|
|
||||||
|
|
||||||
|
def _include_dir_merge_list_yaml(loader, node):
|
||||||
|
"""Load multiple files from directory as a merged list."""
|
||||||
|
path = os.path.join(os.path.dirname(loader.name), node.value)
|
||||||
|
merged_list = []
|
||||||
|
for fname in _find_files(path, '*.yaml'):
|
||||||
|
if os.path.basename(fname) == SECRET_YAML:
|
||||||
|
continue
|
||||||
|
loaded_yaml = load_yaml(fname)
|
||||||
|
if isinstance(loaded_yaml, list):
|
||||||
|
merged_list.extend(loaded_yaml)
|
||||||
|
return _add_reference(merged_list, loader, node)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
def _secret_yaml(loader, node):
|
||||||
|
"""Load secrets and embed it into the configuration YAML."""
|
||||||
|
secret_path = os.path.join(os.path.dirname(loader.name), SECRET_YAML)
|
||||||
|
secrets = load_yaml(secret_path)
|
||||||
|
if node.value not in secrets:
|
||||||
|
raise ESPHomeYAMLError(u"Secret {} not defined".format(node.value))
|
||||||
|
return secrets[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)
|
||||||
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
|
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
|
||||||
|
yaml.SafeLoader.add_constructor('!env_var', _env_var_yaml)
|
||||||
|
yaml.SafeLoader.add_constructor('!secret', _secret_yaml)
|
||||||
|
yaml.SafeLoader.add_constructor('!include', _include_yaml)
|
||||||
|
yaml.SafeLoader.add_constructor('!include_dir_list', _include_dir_list_yaml)
|
||||||
|
yaml.SafeLoader.add_constructor('!include_dir_merge_list',
|
||||||
|
_include_dir_merge_list_yaml)
|
||||||
|
yaml.SafeLoader.add_constructor('!include_dir_named', _include_dir_named_yaml)
|
||||||
|
yaml.SafeLoader.add_constructor('!include_dir_merge_named',
|
||||||
|
_include_dir_merge_named_yaml)
|
||||||
|
|
||||||
|
|
||||||
# From: https://gist.github.com/miracle2k/3184458
|
# From: https://gist.github.com/miracle2k/3184458
|
||||||
|
|||||||
22
pylintrc
Normal file
22
pylintrc
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[MASTER]
|
||||||
|
reports=no
|
||||||
|
|
||||||
|
disable=
|
||||||
|
missing-docstring,
|
||||||
|
fixme,
|
||||||
|
unused-argument,
|
||||||
|
global-statement,
|
||||||
|
too-few-public-methods,
|
||||||
|
too-many-locals,
|
||||||
|
too-many-ancestors,
|
||||||
|
too-many-branches,
|
||||||
|
too-many-statements,
|
||||||
|
too-many-arguments,
|
||||||
|
too-many-return-statements,
|
||||||
|
duplicate-code,
|
||||||
|
|
||||||
|
|
||||||
|
additional-builtins=
|
||||||
|
unicode,
|
||||||
|
long,
|
||||||
|
raw_input
|
||||||
Reference in New Issue
Block a user