esphome/esphome/components/tsl2591/sensor.py
WJCarpenter 183e2a8471
Support component tsl2591 (#2131)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: WJCarpenter <bill@carpenter.org>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-10 10:48:06 +02:00

168 lines
5.7 KiB
Python

# Credit where due....
# I put a certain amount of work into this, but a lot of ESPHome integration is
# "look for other examples and see what they do" programming-by-example. Here are
# things that helped me along with this:
#
# - I mined the existing tsl2561 integration for basic structural framing for both
# the code and documentation.
#
# - I looked at the existing bme280 integration as an example of a single device
# with multiple sensors.
#
# - Comments and code in this thread got me going with the Adafruit TSL2591 library
# and prompted my desired to have tsl2591 as a standard component instead of a
# custom/external component.
#
# - And, of course, the handy and available Adafruit TSL2591 library was very
# helpful in understanding what the device is actually talking about.
#
# Here is the project that started me down the TSL2591 device trail in the first
# place: https://hackaday.io/project/176690-the-water-watcher
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_GAIN,
CONF_ID,
CONF_NAME,
CONF_INTEGRATION_TIME,
CONF_FULL_SPECTRUM,
CONF_INFRARED,
CONF_POWER_SAVE_MODE,
CONF_VISIBLE,
CONF_CALCULATED_LUX,
CONF_DEVICE_FACTOR,
CONF_GLASS_ATTENUATION_FACTOR,
ICON_BRIGHTNESS_6,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_ILLUMINANCE,
STATE_CLASS_MEASUREMENT,
UNIT_EMPTY,
UNIT_LUX,
)
DEPENDENCIES = ["i2c"]
tsl2591_ns = cg.esphome_ns.namespace("tsl2591")
TSL2591IntegrationTime = tsl2591_ns.enum("TSL2591IntegrationTime")
INTEGRATION_TIMES = {
100: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_100MS,
200: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_200MS,
300: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_300MS,
400: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_400MS,
500: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_500MS,
600: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_600MS,
}
TSL2591Gain = tsl2591_ns.enum("TSL2591Gain")
GAINS = {
"1X": TSL2591Gain.TSL2591_GAIN_LOW,
"LOW": TSL2591Gain.TSL2591_GAIN_LOW,
"25X": TSL2591Gain.TSL2591_GAIN_MED,
"MED": TSL2591Gain.TSL2591_GAIN_MED,
"MEDIUM": TSL2591Gain.TSL2591_GAIN_MED,
"400X": TSL2591Gain.TSL2591_GAIN_HIGH,
"HIGH": TSL2591Gain.TSL2591_GAIN_HIGH,
"9500X": TSL2591Gain.TSL2591_GAIN_MAX,
"MAX": TSL2591Gain.TSL2591_GAIN_MAX,
"MAXIMUM": TSL2591Gain.TSL2591_GAIN_MAX,
}
def validate_integration_time(value):
value = cv.positive_time_period_milliseconds(value).total_milliseconds
return cv.enum(INTEGRATION_TIMES, int=True)(value)
TSL2591Component = tsl2591_ns.class_(
"TSL2591Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(TSL2591Component),
cv.Optional(CONF_INFRARED): sensor.sensor_schema(
UNIT_EMPTY,
ICON_BRIGHTNESS_6,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_VISIBLE): sensor.sensor_schema(
UNIT_EMPTY,
ICON_BRIGHTNESS_6,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_FULL_SPECTRUM): sensor.sensor_schema(
UNIT_EMPTY,
ICON_BRIGHTNESS_6,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CALCULATED_LUX): sensor.sensor_schema(
UNIT_LUX,
ICON_BRIGHTNESS_6,
4,
DEVICE_CLASS_ILLUMINANCE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(
CONF_INTEGRATION_TIME, default="100ms"
): validate_integration_time,
cv.Optional(CONF_NAME, default="TLS2591"): cv.string,
cv.Optional(CONF_GAIN, default="MEDIUM"): cv.enum(GAINS, upper=True),
cv.Optional(CONF_POWER_SAVE_MODE, default=True): cv.boolean,
cv.Optional(CONF_DEVICE_FACTOR, default=53.0): cv.float_with_unit(
"device_factor", "", True
),
cv.Optional(CONF_GLASS_ATTENUATION_FACTOR, default=7.7): cv.float_with_unit(
"glass_attenuation_factor", "", True
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x29))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if CONF_FULL_SPECTRUM in config:
conf = config[CONF_FULL_SPECTRUM]
sens = await sensor.new_sensor(conf)
cg.add(var.set_full_spectrum_sensor(sens))
if CONF_INFRARED in config:
conf = config[CONF_INFRARED]
sens = await sensor.new_sensor(conf)
cg.add(var.set_infrared_sensor(sens))
if CONF_VISIBLE in config:
conf = config[CONF_VISIBLE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_visible_sensor(sens))
if CONF_CALCULATED_LUX in config:
conf = config[CONF_CALCULATED_LUX]
sens = await sensor.new_sensor(conf)
cg.add(var.set_calculated_lux_sensor(sens))
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE]))
cg.add(var.set_integration_time(config[CONF_INTEGRATION_TIME]))
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(
var.set_device_and_glass_attenuation_factors(
config[CONF_DEVICE_FACTOR], config[CONF_GLASS_ATTENUATION_FACTOR]
)
)