mirror of
https://github.com/esphome/esphome.git
synced 2025-04-18 00:30:29 +01:00
Displays
This commit is contained in:
parent
36a863b577
commit
19959cf507
56
esphomeyaml/components/display/__init__.py
Normal file
56
esphomeyaml/components/display/__init__.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml.const import CONF_LAMBDA, CONF_ROTATION, CONF_UPDATE_INTERVAL
|
||||||
|
from esphomeyaml.helpers import add, add_job, esphomelib_ns
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
display_ns = esphomelib_ns.namespace('display')
|
||||||
|
DisplayBuffer = display_ns.DisplayBuffer
|
||||||
|
DisplayBufferRef = DisplayBuffer.operator('ref')
|
||||||
|
|
||||||
|
DISPLAY_ROTATIONS = {
|
||||||
|
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
|
||||||
|
90: display_ns.DISPLAY_ROTATION_90_DEGREES,
|
||||||
|
180: display_ns.DISPLAY_ROTATION_180_DEGREES,
|
||||||
|
270: display_ns.DISPLAY_ROTATION_270_DEGREES,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_rotation(value):
|
||||||
|
value = cv.string(value)
|
||||||
|
if value.endswith(u"°"):
|
||||||
|
value = value[:-1]
|
||||||
|
try:
|
||||||
|
value = int(value)
|
||||||
|
except ValueError:
|
||||||
|
raise vol.Invalid(u"Expected integer for rotation")
|
||||||
|
return cv.one_of(*DISPLAY_ROTATIONS)(value)
|
||||||
|
|
||||||
|
|
||||||
|
BASIC_DISPLAY_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
|
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||||
|
})
|
||||||
|
|
||||||
|
FULL_DISPLAY_PLATFORM_SCHEMA = BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||||
|
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||||
|
vol.Optional(CONF_ROTATION): validate_rotation,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def setup_display_core_(display_var, config):
|
||||||
|
if CONF_UPDATE_INTERVAL in config:
|
||||||
|
add(display_var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
|
||||||
|
if CONF_ROTATION in config:
|
||||||
|
add(display_var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]]))
|
||||||
|
|
||||||
|
|
||||||
|
def setup_display(display_var, config):
|
||||||
|
add_job(setup_display_core_, display_var, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_DISPLAY'
|
74
esphomeyaml/components/display/lcd_gpio.py
Normal file
74
esphomeyaml/components/display/lcd_gpio.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml import pins
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.const import CONF_DIMENSIONS, CONF_ENABLE_PIN, CONF_ID, CONF_LAMBDA, CONF_PINS, \
|
||||||
|
CONF_RS_PIN, CONF_RW_PIN
|
||||||
|
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression, process_lambda
|
||||||
|
|
||||||
|
GPIOLCDDisplay = display.display_ns.GPIOLCDDisplay
|
||||||
|
LCDDisplay = display.display_ns.LCDDisplay
|
||||||
|
LCDDisplayRef = LCDDisplay.operator('ref')
|
||||||
|
|
||||||
|
|
||||||
|
def validate_lcd_dimensions(value):
|
||||||
|
value = cv.dimensions(value)
|
||||||
|
if value[0] > 0x40:
|
||||||
|
raise vol.Invalid("LCD displays can't have more than 64 columns")
|
||||||
|
if value[1] > 4:
|
||||||
|
raise vol.Invalid("LCD displays can't have more than 4 rows")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_pin_length(value):
|
||||||
|
if len(value) != 4 and len(value) != 8:
|
||||||
|
raise vol.Invalid("LCD Displays can either operate in 4-pin or 8-pin mode,"
|
||||||
|
"not {}-pin mode".format(len(value)))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_variable_id(GPIOLCDDisplay),
|
||||||
|
vol.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
|
||||||
|
|
||||||
|
vol.Required(CONF_PINS): vol.All([pins.gpio_output_pin_schema], validate_pin_length),
|
||||||
|
vol.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Required(CONF_RS_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema,
|
||||||
|
|
||||||
|
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
rhs = App.make_gpio_lcd_display(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])
|
||||||
|
lcd = Pvariable(config[CONF_ID], rhs)
|
||||||
|
pins_ = []
|
||||||
|
for conf in config[CONF_PINS]:
|
||||||
|
for pin in gpio_output_pin_expression(conf):
|
||||||
|
yield
|
||||||
|
pins_.append(pin)
|
||||||
|
add(lcd.set_data_pins(*pins_))
|
||||||
|
for enable in gpio_output_pin_expression(config[CONF_ENABLE_PIN]):
|
||||||
|
yield
|
||||||
|
add(lcd.set_enable_pin(enable))
|
||||||
|
|
||||||
|
for rs in gpio_output_pin_expression(config[CONF_RS_PIN]):
|
||||||
|
yield
|
||||||
|
add(lcd.set_rs_pin(rs))
|
||||||
|
|
||||||
|
if CONF_RW_PIN in config:
|
||||||
|
for rw in gpio_output_pin_expression(config[CONF_RW_PIN]):
|
||||||
|
yield
|
||||||
|
add(lcd.set_rw_pin(rw))
|
||||||
|
|
||||||
|
if CONF_LAMBDA in config:
|
||||||
|
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]):
|
||||||
|
yield
|
||||||
|
add(lcd.set_writer(lambda_))
|
||||||
|
|
||||||
|
display.setup_display(lcd, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_LCD_DISPLAY'
|
37
esphomeyaml/components/display/lcd_pcf8574.py
Normal file
37
esphomeyaml/components/display/lcd_pcf8574.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.components.display.lcd_gpio import LCDDisplayRef, validate_lcd_dimensions
|
||||||
|
from esphomeyaml.const import CONF_ADDRESS, CONF_DIMENSIONS, CONF_ID, CONF_LAMBDA
|
||||||
|
from esphomeyaml.helpers import App, Pvariable, add, process_lambda
|
||||||
|
|
||||||
|
DEPENDENCIES = ['i2c']
|
||||||
|
|
||||||
|
PCF8574LCDDisplay = display.display_ns.PCF8574LCDDisplay
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_variable_id(PCF8574LCDDisplay),
|
||||||
|
vol.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
|
||||||
|
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||||
|
|
||||||
|
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
rhs = App.make_pcf8574_lcd_display(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])
|
||||||
|
lcd = Pvariable(config[CONF_ID], rhs)
|
||||||
|
|
||||||
|
if CONF_ADDRESS in config:
|
||||||
|
add(lcd.set_address(config[CONF_ADDRESS]))
|
||||||
|
|
||||||
|
if CONF_LAMBDA in config:
|
||||||
|
for lambda_ in process_lambda(config[CONF_LAMBDA], [(LCDDisplayRef, 'it')]):
|
||||||
|
yield
|
||||||
|
add(lcd.set_writer(lambda_))
|
||||||
|
|
||||||
|
display.setup_display(lcd, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = ['-DUSE_LCD_DISPLAY', '-DUSE_LCD_DISPLAY_PCF8574']
|
49
esphomeyaml/components/display/max7129.py
Normal file
49
esphomeyaml/components/display/max7129.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml import pins
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.components.spi import SPIComponent
|
||||||
|
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS, \
|
||||||
|
CONF_SPI_ID
|
||||||
|
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_output_pin_expression, \
|
||||||
|
process_lambda
|
||||||
|
|
||||||
|
DEPENDENCIES = ['spi']
|
||||||
|
|
||||||
|
MAX7219Component = display.display_ns.MAX7219Component
|
||||||
|
MAX7219ComponentRef = MAX7219Component.operator('ref')
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = display.BASIC_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_variable_id(MAX7219Component),
|
||||||
|
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||||
|
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||||
|
|
||||||
|
vol.Optional(CONF_NUM_CHIPS): vol.All(cv.uint8_t, vol.Range(min=1)),
|
||||||
|
vol.Optional(CONF_INTENSITY): vol.All(cv.uint8_t, vol.Range(min=0, max=15)),
|
||||||
|
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
for spi in get_variable(config[CONF_SPI_ID]):
|
||||||
|
yield
|
||||||
|
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||||
|
yield
|
||||||
|
rhs = App.make_max7219(spi, cs)
|
||||||
|
max7219 = Pvariable(config[CONF_ID], rhs)
|
||||||
|
|
||||||
|
if CONF_NUM_CHIPS in config:
|
||||||
|
add(max7219.set_num_chips(config[CONF_NUM_CHIPS]))
|
||||||
|
if CONF_INTENSITY in config:
|
||||||
|
add(max7219.set_intensity(config[CONF_INTENSITY]))
|
||||||
|
|
||||||
|
if CONF_LAMBDA in config:
|
||||||
|
for lambda_ in process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')]):
|
||||||
|
yield
|
||||||
|
add(max7219.set_writer(lambda_))
|
||||||
|
|
||||||
|
display.setup_display(max7219, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_MAX7219'
|
39
esphomeyaml/components/display/ssd1306_i2c.py
Normal file
39
esphomeyaml/components/display/ssd1306_i2c.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml import pins
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.components.display import ssd1306_spi
|
||||||
|
from esphomeyaml.const import CONF_ADDRESS, CONF_EXTERNAL_VCC, CONF_ID, CONF_MODEL, CONF_RESET_PIN
|
||||||
|
from esphomeyaml.helpers import App, Pvariable, add, gpio_output_pin_expression
|
||||||
|
|
||||||
|
DEPENDENCIES = ['i2c']
|
||||||
|
|
||||||
|
I2CSSD1306 = display.display_ns.I2CSSD1306
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_variable_id(I2CSSD1306),
|
||||||
|
vol.Required(CONF_MODEL): cv.one_of(*ssd1306_spi.MODELS),
|
||||||
|
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Optional(CONF_EXTERNAL_VCC): cv.boolean,
|
||||||
|
vol.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
ssd = Pvariable(config[CONF_ID], App.make_i2c_ssd1306())
|
||||||
|
add(ssd.set_model(ssd1306_spi.MODELS[config[CONF_MODEL]]))
|
||||||
|
|
||||||
|
if CONF_RESET_PIN in config:
|
||||||
|
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
|
||||||
|
yield
|
||||||
|
add(ssd.set_reset_pin(reset))
|
||||||
|
if CONF_EXTERNAL_VCC in config:
|
||||||
|
add(ssd.set_external_vcc(config[CONF_EXTERNAL_VCC]))
|
||||||
|
if CONF_ADDRESS in config:
|
||||||
|
add(ssd.set_address(config[CONF_ADDRESS]))
|
||||||
|
|
||||||
|
display.setup_display(ssd, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_SSD1306'
|
57
esphomeyaml/components/display/ssd1306_spi.py
Normal file
57
esphomeyaml/components/display/ssd1306_spi.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml import pins
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.components.spi import SPIComponent
|
||||||
|
from esphomeyaml.const import CONF_CS_PIN, CONF_DC_PIN, CONF_EXTERNAL_VCC, CONF_ID, CONF_MODEL, \
|
||||||
|
CONF_RESET_PIN, CONF_SPI_ID
|
||||||
|
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_output_pin_expression
|
||||||
|
|
||||||
|
DEPENDENCIES = ['spi']
|
||||||
|
|
||||||
|
SPISSD1306 = display.display_ns.SPISSD1306
|
||||||
|
|
||||||
|
MODELS = {
|
||||||
|
'SSD1306_128X32': display.display_ns.SSD1306_MODEL_128_32,
|
||||||
|
'SSD1306_128X64': display.display_ns.SSD1306_MODEL_128_64,
|
||||||
|
'SSD1306_96X16': display.display_ns.SSD1306_MODEL_96_16,
|
||||||
|
'SH1106_128X32': display.display_ns.SH1106_MODEL_128_32,
|
||||||
|
'SH1106_128X64': display.display_ns.SH1106_MODEL_128_64,
|
||||||
|
'SH1106_96X16': display.display_ns.SH1106_MODEL_96_16,
|
||||||
|
}
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_variable_id(SPISSD1306),
|
||||||
|
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||||
|
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Required(CONF_MODEL): cv.one_of(*MODELS),
|
||||||
|
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Optional(CONF_EXTERNAL_VCC): cv.boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
for spi in get_variable(config[CONF_SPI_ID]):
|
||||||
|
yield
|
||||||
|
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||||
|
yield
|
||||||
|
for dc in gpio_output_pin_expression(config[CONF_DC_PIN]):
|
||||||
|
yield
|
||||||
|
|
||||||
|
rhs = App.make_spi_ssd1306(spi, cs, dc)
|
||||||
|
ssd = Pvariable(config[CONF_ID], rhs)
|
||||||
|
add(ssd.set_model(MODELS[config[CONF_MODEL]]))
|
||||||
|
|
||||||
|
if CONF_RESET_PIN in config:
|
||||||
|
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
|
||||||
|
yield
|
||||||
|
add(ssd.set_reset_pin(reset))
|
||||||
|
if CONF_EXTERNAL_VCC in config:
|
||||||
|
add(ssd.set_external_vcc(config[CONF_EXTERNAL_VCC]))
|
||||||
|
|
||||||
|
display.setup_display(ssd, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_SSD1306'
|
84
esphomeyaml/components/display/waveshare_epaper.py
Normal file
84
esphomeyaml/components/display/waveshare_epaper.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml import pins
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.components.spi import SPIComponent
|
||||||
|
from esphomeyaml.const import CONF_BUSY_PIN, CONF_CS_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \
|
||||||
|
CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN, CONF_SPI_ID
|
||||||
|
from esphomeyaml.helpers import App, Pvariable, add, get_variable, gpio_input_pin_expression, \
|
||||||
|
gpio_output_pin_expression, process_lambda
|
||||||
|
|
||||||
|
DEPENDENCIES = ['spi']
|
||||||
|
|
||||||
|
WaveshareEPaperTypeA = display.display_ns.WaveshareEPaperTypeA
|
||||||
|
WaveshareEPaper = display.display_ns.WaveshareEPaper
|
||||||
|
|
||||||
|
MODELS = {
|
||||||
|
'1.54in': ('a', display.display_ns.WAVESHARE_EPAPER_1_54_IN),
|
||||||
|
'2.13in': ('a', display.display_ns.WAVESHARE_EPAPER_2_13_IN),
|
||||||
|
'2.90in': ('a', display.display_ns.WAVESHARE_EPAPER_2_9_IN),
|
||||||
|
'2.70in': ('b', display.display_ns.WAVESHARE_EPAPER_2_7_IN),
|
||||||
|
'4.20in': ('b', display.display_ns.WAVESHARE_EPAPER_4_2_IN),
|
||||||
|
'7.50in': ('b', display.display_ns.WAVESHARE_EPAPER_7_5_IN),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_update_every_only_type_a(value):
|
||||||
|
if CONF_FULL_UPDATE_EVERY not in value:
|
||||||
|
return value
|
||||||
|
if MODELS[value[CONF_MODEL]][0] != 'a':
|
||||||
|
raise vol.Invalid("The 'full_update_every' option is only available for models "
|
||||||
|
"'1.54in', '2.13in' and '2.90in'.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = vol.All(display.FULL_DISPLAY_PLATFORM_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_variable_id(None),
|
||||||
|
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||||
|
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Required(CONF_MODEL): cv.one_of(*MODELS),
|
||||||
|
vol.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||||
|
vol.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema,
|
||||||
|
vol.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t,
|
||||||
|
}), validate_full_update_every_only_type_a)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
for spi in get_variable(config[CONF_SPI_ID]):
|
||||||
|
yield
|
||||||
|
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||||
|
yield
|
||||||
|
for dc in gpio_output_pin_expression(config[CONF_DC_PIN]):
|
||||||
|
yield
|
||||||
|
|
||||||
|
model_type, model = MODELS[config[CONF_MODEL]]
|
||||||
|
if model_type == 'a':
|
||||||
|
rhs = App.make_waveshare_epaper_type_a(spi, cs, dc, model)
|
||||||
|
epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaperTypeA)
|
||||||
|
elif model_type == 'b':
|
||||||
|
rhs = App.make_waveshare_epaper_type_b(spi, cs, dc, model)
|
||||||
|
epaper = Pvariable(config[CONF_ID], rhs, type=WaveshareEPaper)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
if CONF_LAMBDA in config:
|
||||||
|
for lambda_ in process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')]):
|
||||||
|
yield
|
||||||
|
add(epaper.set_writer(lambda_))
|
||||||
|
if CONF_RESET_PIN in config:
|
||||||
|
for reset in gpio_output_pin_expression(config[CONF_RESET_PIN]):
|
||||||
|
yield
|
||||||
|
add(epaper.set_reset_pin(reset))
|
||||||
|
if CONF_BUSY_PIN in config:
|
||||||
|
for reset in gpio_input_pin_expression(config[CONF_BUSY_PIN]):
|
||||||
|
yield
|
||||||
|
add(epaper.set_busy_pin(reset))
|
||||||
|
if CONF_FULL_UPDATE_EVERY in config:
|
||||||
|
add(epaper.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
|
||||||
|
|
||||||
|
display.setup_display(epaper, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_WAVESHARE_EPAPER'
|
126
esphomeyaml/components/font.py
Normal file
126
esphomeyaml/components/font.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml import core
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE
|
||||||
|
from esphomeyaml.core import HexInt
|
||||||
|
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add
|
||||||
|
|
||||||
|
DEPENDENCIES = ['display']
|
||||||
|
|
||||||
|
Font = display.display_ns.Font
|
||||||
|
Glyph = display.display_ns.Glyph
|
||||||
|
|
||||||
|
|
||||||
|
def validate_glyphs(value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = vol.Schema([cv.string])(value)
|
||||||
|
value = vol.Schema([cv.string])(list(value))
|
||||||
|
|
||||||
|
def comparator(x, y):
|
||||||
|
x_ = x.encode('utf-8')
|
||||||
|
y_ = y.encode('utf-8')
|
||||||
|
|
||||||
|
for c in range(min(len(x_), len(y_))):
|
||||||
|
if x_[c] < y_[c]:
|
||||||
|
return -1
|
||||||
|
if x_[c] > y_[c]:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if len(x_) < len(y_):
|
||||||
|
return -1
|
||||||
|
elif len(x_) > len(y_):
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
raise vol.Invalid(u"Found duplicate glyph {}".format(x))
|
||||||
|
|
||||||
|
value.sort(cmp=comparator)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_pillow_installed(value):
|
||||||
|
try:
|
||||||
|
import PIL
|
||||||
|
except ImportError:
|
||||||
|
raise vol.Invalid("Please install the pillow python package to use fonts. "
|
||||||
|
"(pip2 install pillow)")
|
||||||
|
|
||||||
|
if PIL.__version__[0] < '4':
|
||||||
|
raise vol.Invalid("Please update your pillow installation to at least 4.0.x. "
|
||||||
|
"(pip2 install -U pillow)")
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_truetype_file(value):
|
||||||
|
value = cv.string(value)
|
||||||
|
path = os.path.join(core.CONFIG_PATH, value)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
raise vol.Invalid(u"Could not find file '{}'. Please make sure it exists.".format(path))
|
||||||
|
if value.endswith('.zip'): # for Google Fonts downloads
|
||||||
|
raise vol.Invalid(u"Please unzip the font archive '{}' first and then use the .ttf files "
|
||||||
|
u"inside.".format(value))
|
||||||
|
if not value.endswith('.ttf'):
|
||||||
|
raise vol.Invalid(u"Only truetype (.ttf) files are supported. Please make sure you're "
|
||||||
|
u"using the correct format or rename the extension to .ttf")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_GLYPHS = u' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
|
||||||
|
CONF_RAW_DATA_ID = 'raw_data_id'
|
||||||
|
|
||||||
|
FONT_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(CONF_ID): cv.declare_variable_id(Font),
|
||||||
|
vol.Required(CONF_FILE): validate_truetype_file,
|
||||||
|
vol.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
|
||||||
|
vol.Optional(CONF_SIZE, default=12): vol.All(cv.int_, vol.Range(min=1)),
|
||||||
|
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None),
|
||||||
|
})
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.All(validate_pillow_installed, cv.ensure_list, [FONT_SCHEMA])
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
from PIL import ImageFont
|
||||||
|
|
||||||
|
for conf in config:
|
||||||
|
path = os.path.join(core.CONFIG_PATH, conf[CONF_FILE])
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype(path, conf[CONF_SIZE])
|
||||||
|
except Exception as e:
|
||||||
|
raise core.ESPHomeYAMLError(u"Could not load truetype file {}: {}".format(path, e))
|
||||||
|
|
||||||
|
ascent, descent = font.getmetrics()
|
||||||
|
|
||||||
|
glyph_args = {}
|
||||||
|
data = []
|
||||||
|
for glyph in conf[CONF_GLYPHS]:
|
||||||
|
mask = font.getmask(glyph, mode='1')
|
||||||
|
_, (offset_x, offset_y) = font.font.getsize(glyph)
|
||||||
|
width, height = mask.size
|
||||||
|
width8 = ((width + 7) // 8) * 8
|
||||||
|
glyph_data = [0 for _ in range(height * width8 // 8)]
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
if not mask.getpixel((x, y)):
|
||||||
|
continue
|
||||||
|
pos = x + y * width8
|
||||||
|
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
||||||
|
glyph_args[glyph] = (len(data), offset_x, offset_y, width, height)
|
||||||
|
data += glyph_data
|
||||||
|
|
||||||
|
raw_data = MockObj(conf[CONF_RAW_DATA_ID])
|
||||||
|
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
|
||||||
|
raw_data, len(data),
|
||||||
|
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
|
||||||
|
|
||||||
|
glyphs = []
|
||||||
|
for glyph in conf[CONF_GLYPHS]:
|
||||||
|
glyphs.append(Glyph(glyph, raw_data, *glyph_args[glyph]))
|
||||||
|
|
||||||
|
rhs = App.make_font(ArrayInitializer(*glyphs), ascent, ascent + descent)
|
||||||
|
Pvariable(conf[CONF_ID], rhs)
|
85
esphomeyaml/components/image.py
Normal file
85
esphomeyaml/components/image.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import logging
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml import core
|
||||||
|
from esphomeyaml.components import display
|
||||||
|
from esphomeyaml.const import CONF_FILE, CONF_ID, CONF_RESIZE
|
||||||
|
from esphomeyaml.core import HexInt
|
||||||
|
from esphomeyaml.helpers import App, ArrayInitializer, MockObj, Pvariable, RawExpression, add
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEPENDENCIES = ['display']
|
||||||
|
|
||||||
|
Image_ = display.display_ns.Image
|
||||||
|
|
||||||
|
|
||||||
|
def validate_pillow_installed(value):
|
||||||
|
try:
|
||||||
|
# pylint: disable=unused-variable
|
||||||
|
import PIL
|
||||||
|
except ImportError:
|
||||||
|
raise vol.Invalid("Please install the pillow python package to use images. "
|
||||||
|
"(pip2 install pillow)")
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_image_file(value):
|
||||||
|
value = cv.string(value)
|
||||||
|
path = os.path.join(core.CONFIG_PATH, value)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
raise vol.Invalid(u"Could not find file '{}'. Please make sure it exists.".format(path))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
CONF_RAW_DATA_ID = 'raw_data_id'
|
||||||
|
|
||||||
|
FONT_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(CONF_ID): cv.declare_variable_id(Image_),
|
||||||
|
vol.Required(CONF_FILE): validate_image_file,
|
||||||
|
vol.Optional(CONF_RESIZE): cv.dimensions,
|
||||||
|
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_variable_id(None),
|
||||||
|
})
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.All(validate_pillow_installed, cv.ensure_list, [FONT_SCHEMA])
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
for conf in config:
|
||||||
|
path = os.path.join(core.CONFIG_PATH, conf[CONF_FILE])
|
||||||
|
try:
|
||||||
|
image = Image.open(path)
|
||||||
|
except Exception as e:
|
||||||
|
raise core.ESPHomeYAMLError(u"Could not load image file {}: {}".format(path, e))
|
||||||
|
|
||||||
|
if CONF_RESIZE in conf:
|
||||||
|
image.thumbnail(conf[CONF_RESIZE])
|
||||||
|
|
||||||
|
image = image.convert('1')
|
||||||
|
width, height = image.size
|
||||||
|
if width > 500 or height > 500:
|
||||||
|
_LOGGER.warning("The image you requested is very big. Please consider using the resize "
|
||||||
|
"parameter")
|
||||||
|
width8 = ((width + 7) // 8) * 8
|
||||||
|
data = [0 for _ in range(height * width8 // 8)]
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
if image.getpixel((x, y)):
|
||||||
|
continue
|
||||||
|
pos = x + y * width8
|
||||||
|
data[pos // 8] |= 0x80 >> (pos % 8)
|
||||||
|
|
||||||
|
raw_data = MockObj(conf[CONF_RAW_DATA_ID])
|
||||||
|
add(RawExpression('static const uint8_t {}[{}] PROGMEM = {}'.format(
|
||||||
|
raw_data, len(data),
|
||||||
|
ArrayInitializer(*[HexInt(x) for x in data], multiline=False))))
|
||||||
|
|
||||||
|
rhs = App.make_image(raw_data, width, height)
|
||||||
|
Pvariable(conf[CONF_ID], rhs)
|
@ -4,7 +4,7 @@ import esphomeyaml.config_validation as cv
|
|||||||
from esphomeyaml import pins
|
from esphomeyaml import pins
|
||||||
from esphomeyaml.components import binary_sensor
|
from esphomeyaml.components import binary_sensor
|
||||||
from esphomeyaml.components.spi import SPIComponent
|
from esphomeyaml.components.spi import SPIComponent
|
||||||
from esphomeyaml.const import CONF_CS, CONF_ID, CONF_SPI_ID, CONF_UPDATE_INTERVAL
|
from esphomeyaml.const import CONF_CS_PIN, CONF_ID, CONF_SPI_ID, CONF_UPDATE_INTERVAL
|
||||||
from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression
|
from esphomeyaml.helpers import App, Pvariable, get_variable, gpio_output_pin_expression
|
||||||
|
|
||||||
DEPENDENCIES = ['spi']
|
DEPENDENCIES = ['spi']
|
||||||
@ -14,7 +14,7 @@ PN532Component = binary_sensor.binary_sensor_ns.PN532Component
|
|||||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
|
CONFIG_SCHEMA = vol.All(cv.ensure_list, [vol.Schema({
|
||||||
cv.GenerateID(): cv.declare_variable_id(PN532Component),
|
cv.GenerateID(): cv.declare_variable_id(PN532Component),
|
||||||
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||||
vol.Required(CONF_CS): pins.gpio_output_pin_schema,
|
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||||
})])
|
})])
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ def to_code(config):
|
|||||||
for spi in get_variable(conf[CONF_SPI_ID]):
|
for spi in get_variable(conf[CONF_SPI_ID]):
|
||||||
yield
|
yield
|
||||||
cs = None
|
cs = None
|
||||||
for cs in gpio_output_pin_expression(conf[CONF_CS]):
|
for cs in gpio_output_pin_expression(conf[CONF_CS_PIN]):
|
||||||
yield
|
yield
|
||||||
rhs = App.make_pn532_component(spi, cs, conf.get(CONF_UPDATE_INTERVAL))
|
rhs = App.make_pn532_component(spi, cs, conf.get(CONF_UPDATE_INTERVAL))
|
||||||
Pvariable(conf[CONF_ID], rhs)
|
Pvariable(conf[CONF_ID], rhs)
|
||||||
|
@ -4,7 +4,8 @@ import esphomeyaml.config_validation as cv
|
|||||||
from esphomeyaml import pins
|
from esphomeyaml import pins
|
||||||
from esphomeyaml.components import sensor
|
from esphomeyaml.components import sensor
|
||||||
from esphomeyaml.components.spi import SPIComponent
|
from esphomeyaml.components.spi import SPIComponent
|
||||||
from esphomeyaml.const import CONF_CS, CONF_MAKE_ID, CONF_NAME, CONF_SPI_ID, CONF_UPDATE_INTERVAL
|
from esphomeyaml.const import CONF_CS_PIN, CONF_MAKE_ID, CONF_NAME, CONF_SPI_ID, \
|
||||||
|
CONF_UPDATE_INTERVAL
|
||||||
from esphomeyaml.helpers import App, Application, get_variable, gpio_output_pin_expression, variable
|
from esphomeyaml.helpers import App, Application, get_variable, gpio_output_pin_expression, variable
|
||||||
|
|
||||||
MakeMAX6675Sensor = Application.MakeMAX6675Sensor
|
MakeMAX6675Sensor = Application.MakeMAX6675Sensor
|
||||||
@ -12,7 +13,7 @@ MakeMAX6675Sensor = Application.MakeMAX6675Sensor
|
|||||||
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({
|
||||||
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor),
|
cv.GenerateID(CONF_MAKE_ID): cv.declare_variable_id(MakeMAX6675Sensor),
|
||||||
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent),
|
||||||
vol.Required(CONF_CS): pins.gpio_output_pin_schema,
|
vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
||||||
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ def to_code(config):
|
|||||||
for spi in get_variable(config[CONF_SPI_ID]):
|
for spi in get_variable(config[CONF_SPI_ID]):
|
||||||
yield
|
yield
|
||||||
cs = None
|
cs = None
|
||||||
for cs in gpio_output_pin_expression(config[CONF_CS]):
|
for cs in gpio_output_pin_expression(config[CONF_CS_PIN]):
|
||||||
yield
|
yield
|
||||||
rhs = App.make_max6675_sensor(config[CONF_NAME], spi, cs,
|
rhs = App.make_max6675_sensor(config[CONF_NAME], spi, cs,
|
||||||
config.get(CONF_UPDATE_INTERVAL))
|
config.get(CONF_UPDATE_INTERVAL))
|
||||||
|
@ -2,18 +2,18 @@ import voluptuous as vol
|
|||||||
|
|
||||||
import esphomeyaml.config_validation as cv
|
import esphomeyaml.config_validation as cv
|
||||||
from esphomeyaml import pins
|
from esphomeyaml import pins
|
||||||
from esphomeyaml.const import CONF_CLK, CONF_ID, CONF_MISO, CONF_MOSI
|
from esphomeyaml.const import CONF_CLK_PIN, CONF_ID, CONF_MISO_PIN, CONF_MOSI_PIN
|
||||||
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_input_pin_expression, \
|
from esphomeyaml.helpers import App, Pvariable, esphomelib_ns, gpio_input_pin_expression, \
|
||||||
gpio_output_pin_expression
|
gpio_output_pin_expression, add
|
||||||
|
|
||||||
SPIComponent = esphomelib_ns.SPIComponent
|
SPIComponent = esphomelib_ns.SPIComponent
|
||||||
|
|
||||||
SPI_SCHEMA = vol.Schema({
|
SPI_SCHEMA = vol.All(vol.Schema({
|
||||||
cv.GenerateID(): cv.declare_variable_id(SPIComponent),
|
cv.GenerateID(): cv.declare_variable_id(SPIComponent),
|
||||||
vol.Required(CONF_CLK): pins.gpio_output_pin_schema,
|
vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
|
||||||
vol.Required(CONF_MISO): pins.gpio_input_pin_schema,
|
vol.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema,
|
||||||
vol.Optional(CONF_MOSI): pins.gpio_output_pin_schema,
|
vol.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema,
|
||||||
})
|
}), cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN))
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA])
|
CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA])
|
||||||
|
|
||||||
@ -21,17 +21,18 @@ CONFIG_SCHEMA = vol.All(cv.ensure_list, [SPI_SCHEMA])
|
|||||||
def to_code(config):
|
def to_code(config):
|
||||||
for conf in config:
|
for conf in config:
|
||||||
clk = None
|
clk = None
|
||||||
for clk in gpio_output_pin_expression(conf[CONF_CLK]):
|
for clk in gpio_output_pin_expression(conf[CONF_CLK_PIN]):
|
||||||
yield
|
yield
|
||||||
miso = None
|
rhs = App.init_spi(clk)
|
||||||
for miso in gpio_input_pin_expression(conf[CONF_MISO]):
|
spi = Pvariable(conf[CONF_ID], rhs)
|
||||||
yield
|
if CONF_MISO_PIN in conf:
|
||||||
mosi = None
|
for miso in gpio_input_pin_expression(conf[CONF_MISO_PIN]):
|
||||||
if CONF_MOSI in conf:
|
|
||||||
for mosi in gpio_output_pin_expression(conf[CONF_MOSI]):
|
|
||||||
yield
|
yield
|
||||||
rhs = App.init_spi(clk, miso, mosi)
|
add(spi.set_miso(miso))
|
||||||
Pvariable(conf[CONF_ID], rhs)
|
if CONF_MOSI_PIN in conf:
|
||||||
|
for mosi in gpio_input_pin_expression(conf[CONF_MOSI_PIN]):
|
||||||
|
yield
|
||||||
|
add(spi.set_mosi(mosi))
|
||||||
|
|
||||||
|
|
||||||
BUILD_FLAGS = '-DUSE_SPI'
|
BUILD_FLAGS = '-DUSE_SPI'
|
||||||
|
109
esphomeyaml/components/time/__init__.py
Normal file
109
esphomeyaml/components/time/__init__.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml.const import CONF_TIMEZONE
|
||||||
|
from esphomeyaml.helpers import add, add_job, esphomelib_ns
|
||||||
|
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
time_ns = esphomelib_ns.namespace('time')
|
||||||
|
|
||||||
|
|
||||||
|
def _tz_timedelta(td):
|
||||||
|
offset_hour = int(td.total_seconds() / (60 * 60))
|
||||||
|
offset_minute = int(abs(td.total_seconds() / 60)) % 60
|
||||||
|
offset_second = int(abs(td.total_seconds())) % 60
|
||||||
|
if offset_hour == 0 and offset_minute == 0 and offset_second == 0:
|
||||||
|
return '0'
|
||||||
|
elif offset_minute == 0 and offset_second == 0:
|
||||||
|
return '{}'.format(offset_hour)
|
||||||
|
elif offset_second == 0:
|
||||||
|
return '{}:{}'.format(offset_hour, offset_minute)
|
||||||
|
return '{}:{}:{}'.format(offset_hour, offset_minute, offset_second)
|
||||||
|
|
||||||
|
|
||||||
|
# https://stackoverflow.com/a/16804556/8924614
|
||||||
|
def _week_of_month(dt):
|
||||||
|
first_day = dt.replace(day=1)
|
||||||
|
dom = dt.day
|
||||||
|
adjusted_dom = dom + first_day.weekday()
|
||||||
|
return int(math.ceil(adjusted_dom / 7.0))
|
||||||
|
|
||||||
|
|
||||||
|
def _tz_dst_str(dt):
|
||||||
|
td = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second)
|
||||||
|
return 'M{}.{}.{}/{}'.format(dt.month, _week_of_month(dt), dt.isoweekday() % 7,
|
||||||
|
_tz_timedelta(td))
|
||||||
|
|
||||||
|
|
||||||
|
def detect_tz():
|
||||||
|
try:
|
||||||
|
import tzlocal
|
||||||
|
except ImportError:
|
||||||
|
raise vol.Invalid("No timezone specified and 'tzlocal' not installed. To automatically "
|
||||||
|
"detect the timezone please install tzlocal (pip2 install tzlocal)")
|
||||||
|
tz = tzlocal.get_localzone()
|
||||||
|
dst_begins = None
|
||||||
|
dst_tzname = None
|
||||||
|
dst_utcoffset = None
|
||||||
|
dst_ends = None
|
||||||
|
norm_tzname = None
|
||||||
|
norm_utcoffset = None
|
||||||
|
|
||||||
|
hour = datetime.timedelta(hours=1)
|
||||||
|
this_year = datetime.datetime.now().year
|
||||||
|
dt = datetime.datetime(year=this_year, month=1, day=1)
|
||||||
|
last_dst = None
|
||||||
|
while dt.year == this_year:
|
||||||
|
current_dst = tz.dst(dt, is_dst=not last_dst)
|
||||||
|
is_dst = bool(current_dst)
|
||||||
|
if is_dst != last_dst:
|
||||||
|
if is_dst:
|
||||||
|
dst_begins = dt
|
||||||
|
dst_tzname = tz.tzname(dt, is_dst=True)
|
||||||
|
dst_utcoffset = tz.utcoffset(dt, is_dst=True)
|
||||||
|
else:
|
||||||
|
dst_ends = dt + hour
|
||||||
|
norm_tzname = tz.tzname(dt, is_dst=False)
|
||||||
|
norm_utcoffset = tz.utcoffset(dt, is_dst=False)
|
||||||
|
last_dst = is_dst
|
||||||
|
dt += hour
|
||||||
|
|
||||||
|
tzbase = '{}{}'.format(norm_tzname, _tz_timedelta(-1 * norm_utcoffset))
|
||||||
|
if dst_begins is None:
|
||||||
|
# No DST in this timezone
|
||||||
|
_LOGGER.info("Auto-detected timezone '%s' with UTC offset %s",
|
||||||
|
norm_tzname, _tz_timedelta(norm_utcoffset))
|
||||||
|
return tzbase
|
||||||
|
tzext = '{}{},{},{}'.format(dst_tzname, _tz_timedelta(-1 * dst_utcoffset),
|
||||||
|
_tz_dst_str(dst_begins), _tz_dst_str(dst_ends))
|
||||||
|
_LOGGER.info("Auto-detected timezone '%s' with UTC offset %s and daylight savings time from "
|
||||||
|
"%s to %s",
|
||||||
|
norm_tzname, _tz_timedelta(norm_utcoffset), dst_begins.strftime("%x %X"),
|
||||||
|
dst_ends.strftime("%x %X"))
|
||||||
|
return tzbase + tzext
|
||||||
|
|
||||||
|
|
||||||
|
TIME_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
|
vol.Optional(CONF_TIMEZONE, default=detect_tz): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def setup_time_core_(time_var, config):
|
||||||
|
add(time_var.set_timezone(config[CONF_TIMEZONE]))
|
||||||
|
|
||||||
|
|
||||||
|
def setup_time(time_var, config):
|
||||||
|
add_job(setup_time_core_, time_var, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_TIME'
|
24
esphomeyaml/components/time/sntp.py
Normal file
24
esphomeyaml/components/time/sntp.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import esphomeyaml.config_validation as cv
|
||||||
|
from esphomeyaml.components import time as time_
|
||||||
|
from esphomeyaml.const import CONF_ID, CONF_LAMBDA, CONF_SERVERS
|
||||||
|
from esphomeyaml.helpers import App, Pvariable
|
||||||
|
|
||||||
|
SNTPComponent = time_.time_ns.SNTPComponent
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = time_.TIME_PLATFORM_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_variable_id(SNTPComponent),
|
||||||
|
vol.Optional(CONF_SERVERS): vol.All(cv.ensure_list, [cv.string], vol.Length(max=3)),
|
||||||
|
vol.Optional(CONF_LAMBDA): cv.lambda_,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
rhs = App.make_sntp_component(*config.get(CONF_SERVERS, []))
|
||||||
|
sntp = Pvariable(config[CONF_ID], rhs)
|
||||||
|
|
||||||
|
time_.setup_time(sntp, config)
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_FLAGS = '-DUSE_SNTP_COMPONENT'
|
@ -102,7 +102,7 @@ def boolean(value):
|
|||||||
|
|
||||||
def ensure_list(value):
|
def ensure_list(value):
|
||||||
"""Wrap value in list if it is not one."""
|
"""Wrap value in list if it is not one."""
|
||||||
if value is None:
|
if value is None or (isinstance(value, dict) and not value):
|
||||||
return []
|
return []
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
return value
|
return value
|
||||||
@ -566,6 +566,24 @@ def lambda_(value):
|
|||||||
return Lambda(string_strict(value))
|
return Lambda(string_strict(value))
|
||||||
|
|
||||||
|
|
||||||
|
def dimensions(value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
if len(value) != 2:
|
||||||
|
raise vol.Invalid(u"Dimensions must have a length of two, not {}".format(len(value)))
|
||||||
|
try:
|
||||||
|
width, height = int(value[0]), int(value[1])
|
||||||
|
except ValueError:
|
||||||
|
raise vol.Invalid(u"Width and height dimensions must be integers")
|
||||||
|
if width <= 0 or height <= 0:
|
||||||
|
raise vol.Invalid(u"Width and height must at least be 1")
|
||||||
|
return [width, height]
|
||||||
|
value = string(value)
|
||||||
|
match = re.match(r"\s*([0-9]+)\s*[xX]\s*([0-9]+)\s*", value)
|
||||||
|
if not match:
|
||||||
|
raise vol.Invalid(u"Invalid value '{}' for dimensions. Only WIDTHxHEIGHT is allowed.")
|
||||||
|
return dimensions([match.group(1), match.group(2)])
|
||||||
|
|
||||||
|
|
||||||
REGISTERED_IDS = set()
|
REGISTERED_IDS = set()
|
||||||
|
|
||||||
|
|
||||||
|
@ -220,10 +220,10 @@ CONF_ON_VALUE = 'on_value'
|
|||||||
CONF_ON_RAW_VALUE = 'on_raw_value'
|
CONF_ON_RAW_VALUE = 'on_raw_value'
|
||||||
CONF_ON_VALUE_RANGE = 'on_value_range'
|
CONF_ON_VALUE_RANGE = 'on_value_range'
|
||||||
CONF_ON_MESSAGE = 'on_message'
|
CONF_ON_MESSAGE = 'on_message'
|
||||||
CONF_CS = 'cs'
|
CONF_CS_PIN = 'cs_pin'
|
||||||
CONF_CLK = 'clk'
|
CONF_CLK_PIN = 'clk_pin'
|
||||||
CONF_MISO = 'miso'
|
CONF_MISO_PIN = 'miso_pin'
|
||||||
CONF_MOSI = 'mosi'
|
CONF_MOSI_PIN = 'mosi_pin'
|
||||||
CONF_TURN_ON_ACTION = 'turn_on_action'
|
CONF_TURN_ON_ACTION = 'turn_on_action'
|
||||||
CONF_TURN_OFF_ACTION = 'turn_off_action'
|
CONF_TURN_OFF_ACTION = 'turn_off_action'
|
||||||
CONF_OPEN_ACTION = 'open_action'
|
CONF_OPEN_ACTION = 'open_action'
|
||||||
@ -289,6 +289,25 @@ CONF_ONE = 'one'
|
|||||||
CONF_GROUP = 'group'
|
CONF_GROUP = 'group'
|
||||||
CONF_DEVICE = 'device'
|
CONF_DEVICE = 'device'
|
||||||
CONF_FAMILY = 'family'
|
CONF_FAMILY = 'family'
|
||||||
|
CONF_FILE = 'file'
|
||||||
|
CONF_GLYPHS = 'glyphs'
|
||||||
|
CONF_SIZE = 'size'
|
||||||
|
CONF_RESIZE = 'resize'
|
||||||
|
CONF_ROTATION = 'rotation'
|
||||||
|
CONF_DC_PIN = 'dc_pin'
|
||||||
|
CONF_RESET_PIN = 'reset_pin'
|
||||||
|
CONF_BUSY_PIN = 'busy_pin'
|
||||||
|
CONF_FULL_UPDATE_EVERY = 'full_update_every'
|
||||||
|
CONF_PINS = 'pins'
|
||||||
|
CONF_ENABLE_PIN = 'enable_pin'
|
||||||
|
CONF_RS_PIN = 'rs_pin'
|
||||||
|
CONF_RW_PIN = 'rw_pin'
|
||||||
|
CONF_DIMENSIONS = 'dimensions'
|
||||||
|
CONF_NUM_CHIPS = 'num_chips'
|
||||||
|
CONF_INTENSITY = 'intensity'
|
||||||
|
CONF_EXTERNAL_VCC = 'external_vcc'
|
||||||
|
CONF_TIMEZONE = 'timezone'
|
||||||
|
CONF_SERVERS = 'servers'
|
||||||
|
|
||||||
ESP32_BOARDS = [
|
ESP32_BOARDS = [
|
||||||
'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',
|
'featheresp32', 'node32s', 'espea32', 'firebeetle32', 'esp32doit-devkit-v1',
|
||||||
|
@ -10,6 +10,8 @@ class ESPHomeYAMLError(Exception):
|
|||||||
|
|
||||||
class HexInt(long):
|
class HexInt(long):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
if 0 <= self <= 255:
|
||||||
|
return "0x{:02X}".format(self)
|
||||||
return "0x{:X}".format(self)
|
return "0x{:X}".format(self)
|
||||||
|
|
||||||
|
|
||||||
@ -181,8 +183,8 @@ class TimePeriodSeconds(TimePeriod):
|
|||||||
class Lambda(object):
|
class Lambda(object):
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
self.parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)\.', value)
|
self.parts = re.split(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)', value)
|
||||||
self.requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 2)]
|
self.requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
@ -226,7 +226,7 @@ class LambdaExpression(Expression):
|
|||||||
self.return_type = return_type
|
self.return_type = return_type
|
||||||
if return_type is not None:
|
if return_type is not None:
|
||||||
self.requires.append(return_type)
|
self.requires.append(return_type)
|
||||||
for i in range(1, len(parts), 2):
|
for i in range(1, len(parts), 3):
|
||||||
self.requires.append(parts[i])
|
self.requires.append(parts[i])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -411,7 +411,11 @@ def process_lambda(value, parameters, capture='=', return_type=None):
|
|||||||
var = None
|
var = None
|
||||||
for var in get_variable(id):
|
for var in get_variable(id):
|
||||||
yield
|
yield
|
||||||
parts[i*2 + 1] = var._
|
if parts[i * 3 + 2] == '.':
|
||||||
|
parts[i * 3 + 1] = var._
|
||||||
|
else:
|
||||||
|
parts[i * 3 + 1] = var
|
||||||
|
parts[i * 3 + 2] = ''
|
||||||
yield LambdaExpression(parts, parameters, capture, return_type)
|
yield LambdaExpression(parts, parameters, capture, return_type)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -522,6 +526,13 @@ class MockObj(Expression):
|
|||||||
obj.requires.append(self)
|
obj.requires.append(self)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
def operator(self, name):
|
||||||
|
if name == 'ref':
|
||||||
|
obj = MockObj(u'{} &'.format(self.base), u'')
|
||||||
|
obj.requires.append(self)
|
||||||
|
return obj
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
def has_side_effects(self):
|
def has_side_effects(self):
|
||||||
return self._has_side_effects
|
return self._has_side_effects
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user