forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add LaCrosse sensor platform (home-assistant#10195)
* Initial commit of LaCrosse sensor component Signed-off-by: Heiko Thiery <[email protected]> * fix review comments from houndci-bot Signed-off-by: Heiko Thiery <[email protected]> * fix review comments from houndci-bot Signed-off-by: Heiko Thiery <[email protected]> * add pylacrosse version to REQUIREMENTS Signed-off-by: Heiko Thiery <[email protected]> * add lacrosse to .coveragerc Signed-off-by: Heiko Thiery <[email protected]> * import 3rd party libraries inside methods Signed-off-by: Heiko Thiery <[email protected]> * add pylacrosse to requirements_all.txt Signed-off-by: Heiko Thiery <[email protected]> * add missing docstring Signed-off-by: Heiko Thiery <[email protected]> * fix pylint warning Signed-off-by: Heiko Thiery <[email protected]> * fix pylint warning Signed-off-by: Heiko Thiery <[email protected]> * fix pylint warnings Signed-off-by: Heiko Thiery <[email protected]> * remove too many blank lines Signed-off-by: Heiko Thiery <[email protected]> * some minor cleanup Signed-off-by: Heiko Thiery <[email protected]> * change to single quote Signed-off-by: Heiko Thiery <[email protected]> * incorporate review comments Signed-off-by: Heiko Thiery <[email protected]> * remove type check as validation only allows TYPES Signed-off-by: Heiko Thiery <[email protected]> * Adjust log level and update ordering
- Loading branch information
Showing
3 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
""" | ||
Support for LaCrosse sensor components. | ||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/sensor.lacrosse/ | ||
""" | ||
import logging | ||
from datetime import timedelta | ||
|
||
import voluptuous as vol | ||
|
||
import homeassistant.helpers.config_validation as cv | ||
from homeassistant.core import callback | ||
from homeassistant.components.sensor import (ENTITY_ID_FORMAT, PLATFORM_SCHEMA) | ||
from homeassistant.const import ( | ||
EVENT_HOMEASSISTANT_STOP, CONF_DEVICE, CONF_NAME, CONF_ID, | ||
CONF_SENSORS, CONF_TYPE, TEMP_CELSIUS) | ||
from homeassistant.helpers.entity import Entity, async_generate_entity_id | ||
from homeassistant.helpers.event import async_track_point_in_utc_time | ||
from homeassistant.util import dt as dt_util | ||
|
||
REQUIREMENTS = ['pylacrosse==0.2.7'] | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
CONF_BAUD = 'baud' | ||
CONF_EXPIRE_AFTER = 'expire_after' | ||
|
||
DEFAULT_DEVICE = '/dev/ttyUSB0' | ||
DEFAULT_BAUD = '57600' | ||
DEFAULT_EXPIRE_AFTER = 300 | ||
|
||
TYPES = ['battery', 'humidity', 'temperature'] | ||
|
||
SENSOR_SCHEMA = vol.Schema({ | ||
vol.Required(CONF_ID): cv.positive_int, | ||
vol.Required(CONF_TYPE): vol.In(TYPES), | ||
vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, | ||
vol.Optional(CONF_NAME): cv.string, | ||
}) | ||
|
||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
vol.Required(CONF_SENSORS): vol.Schema({cv.slug: SENSOR_SCHEMA}), | ||
vol.Optional(CONF_BAUD, default=DEFAULT_BAUD): cv.string, | ||
vol.Optional(CONF_DEVICE, default=DEFAULT_DEVICE): cv.string, | ||
}) | ||
|
||
|
||
def setup_platform(hass, config, add_devices, discovery_info=None): | ||
"""Set up the LaCrosse sensors.""" | ||
import pylacrosse | ||
from serial import SerialException | ||
|
||
usb_device = config.get(CONF_DEVICE) | ||
baud = int(config.get(CONF_BAUD)) | ||
expire_after = config.get(CONF_EXPIRE_AFTER) | ||
|
||
_LOGGER.debug("%s %s", usb_device, baud) | ||
|
||
try: | ||
lacrosse = pylacrosse.LaCrosse(usb_device, baud) | ||
lacrosse.open() | ||
except SerialException as exc: | ||
_LOGGER.warning("Unable to open serial port: %s", exc) | ||
return False | ||
|
||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, lacrosse.close) | ||
|
||
sensors = [] | ||
for device, device_config in config[CONF_SENSORS].items(): | ||
_LOGGER.debug("%s %s", device, device_config) | ||
|
||
typ = device_config.get(CONF_TYPE) | ||
sensor_class = TYPE_CLASSES[typ] | ||
name = device_config.get(CONF_NAME, device) | ||
|
||
sensors.append( | ||
sensor_class( | ||
hass, lacrosse, device, name, expire_after, device_config | ||
) | ||
) | ||
|
||
add_devices(sensors) | ||
|
||
|
||
class LaCrosseSensor(Entity): | ||
"""Implementation of a Lacrosse sensor.""" | ||
|
||
_temperature = None | ||
_humidity = None | ||
_low_battery = None | ||
_new_battery = None | ||
|
||
def __init__(self, hass, lacrosse, device_id, name, expire_after, config): | ||
"""Initialize the sensor.""" | ||
self.hass = hass | ||
self.entity_id = async_generate_entity_id( | ||
ENTITY_ID_FORMAT, device_id, hass=hass) | ||
self._config = config | ||
self._name = name | ||
self._value = None | ||
self._expire_after = expire_after | ||
self._expiration_trigger = None | ||
|
||
lacrosse.register_callback( | ||
int(self._config['id']), self._callback_lacrosse, None) | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the sensor.""" | ||
return self._name | ||
|
||
def update(self, *args): | ||
"""Get the latest data.""" | ||
pass | ||
|
||
@property | ||
def device_state_attributes(self): | ||
"""Return the state attributes.""" | ||
attributes = { | ||
'low_battery': self._low_battery, | ||
'new_battery': self._new_battery, | ||
} | ||
return attributes | ||
|
||
def _callback_lacrosse(self, lacrosse_sensor, user_data): | ||
"""Callback function that is called from pylacrosse with new values.""" | ||
if self._expire_after is not None and self._expire_after > 0: | ||
# Reset old trigger | ||
if self._expiration_trigger: | ||
self._expiration_trigger() | ||
self._expiration_trigger = None | ||
|
||
# Set new trigger | ||
expiration_at = ( | ||
dt_util.utcnow() + timedelta(seconds=self._expire_after)) | ||
|
||
self._expiration_trigger = async_track_point_in_utc_time( | ||
self.hass, self.value_is_expired, expiration_at) | ||
|
||
self._temperature = round(lacrosse_sensor.temperature * 2) / 2 | ||
self._humidity = lacrosse_sensor.humidity | ||
self._low_battery = lacrosse_sensor.low_battery | ||
self._new_battery = lacrosse_sensor.new_battery | ||
|
||
@callback | ||
def value_is_expired(self, *_): | ||
"""Triggered when value is expired.""" | ||
self._expiration_trigger = None | ||
self._value = None | ||
self.async_schedule_update_ha_state() | ||
|
||
|
||
class LaCrosseTemperature(LaCrosseSensor): | ||
"""Implementation of a Lacrosse temperature sensor.""" | ||
|
||
@property | ||
def unit_of_measurement(self): | ||
"""Return the unit of measurement.""" | ||
return TEMP_CELSIUS | ||
|
||
@property | ||
def state(self): | ||
"""Return the state of the sensor.""" | ||
return self._temperature | ||
|
||
|
||
class LaCrosseHumidity(LaCrosseSensor): | ||
"""Implementation of a Lacrosse humidity sensor.""" | ||
|
||
@property | ||
def unit_of_measurement(self): | ||
"""Return the unit of measurement.""" | ||
return '%' | ||
|
||
@property | ||
def state(self): | ||
"""Return the state of the sensor.""" | ||
return self._humidity | ||
|
||
@property | ||
def icon(self): | ||
"""Icon to use in the frontend.""" | ||
return 'mdi:water-percent' | ||
|
||
|
||
class LaCrosseBattery(LaCrosseSensor): | ||
"""Implementation of a Lacrosse battery sensor.""" | ||
|
||
@property | ||
def state(self): | ||
"""Return the state of the sensor.""" | ||
if self._low_battery is None: | ||
state = None | ||
elif self._low_battery is True: | ||
state = 'low' | ||
else: | ||
state = 'ok' | ||
return state | ||
|
||
@property | ||
def icon(self): | ||
"""Icon to use in the frontend.""" | ||
if self._low_battery is None: | ||
icon = 'mdi:battery-unknown' | ||
elif self._low_battery is True: | ||
icon = 'mdi:battery-alert' | ||
else: | ||
icon = 'mdi:battery' | ||
return icon | ||
|
||
|
||
TYPE_CLASSES = { | ||
'temperature': LaCrosseTemperature, | ||
'humidity': LaCrosseHumidity, | ||
'battery': LaCrosseBattery | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters