"""Support for covers from FHEM""" import voluptuous as vol import logging from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorDevice, \ DEVICE_CLASS_MOTION, DEVICE_CLASS_OPENING, DEVICE_CLASS_BATTERY from homeassistant.const import CONF_NAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_call_later from homeassistant.core import callback from . import DATA_FHEM, device_error_reporting, CONF_FHEM_SENSOR_TYPE, CONF_FHEM_IDS _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_FHEM_IDS): vol.All(cv.ensure_list, [cv.string]), vol.Required(CONF_NAME): cv.string, vol.Required(CONF_FHEM_SENSOR_TYPE): vol.In(('motion', 'cover', 'battery')), }) async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): connection = hass.data[DATA_FHEM] sensor = FhemBinarySensor(connection, config[CONF_NAME], config[CONF_FHEM_IDS], config[CONF_FHEM_SENSOR_TYPE]) for dev_id in config[CONF_FHEM_IDS]: connection.register_device(dev_id, sensor) async_add_entities([sensor]) class FhemBinarySensor(BinarySensorDevice): def __init__(self, connection, name, ids, sensor_type): self._on = None self.connection = connection self._ids = ids self._name = name self._type = sensor_type self._available = True self._state = None self._unsubscribe_motion_off = None @property def name(self): return self._name @property def should_poll(self): """No polling needed.""" return False @property def available(self) -> bool: return self._available and self.connection.connected @property def is_on(self): return self._state def refresh(self): pass async def line_received(self, line): if self._type == 'motion' and line.startswith('motion'): self._available = True self._state = True self._start_motion_off_callback(delay_in_seconds=31) await self.async_update_ha_state() elif self._type == 'cover' and line.startswith('cover'): self._available = True _, new_value = line.split(':') self._state = not (new_value.strip().lower() == 'closed') await self.async_update_ha_state() elif self._type == 'battery' and line.startswith('battery'): self._available = True _, new_value = line.split(':') self._state = not (new_value.strip().lower() == 'ok') await self.async_update_ha_state() elif line.startswith('ResndFail') or line.startswith('MISSING ACK'): self._available = False await self.async_update_ha_state() else: device_error_reporting(self.hass, line, component_type="Switch", component_name=self.entity_id) @property def device_state_attributes(self): return None def _start_motion_off_callback(self, delay_in_seconds): self._stop_motion_off_callback() self._unsubscribe_motion_off = async_call_later(self.hass, delay_in_seconds, self._stop_motion_callback) def _stop_motion_off_callback(self): if self._unsubscribe_motion_off is not None: self._unsubscribe_motion_off() self._unsubscribe_motion_off = None @callback def _stop_motion_callback(self, now): self._state = False self.schedule_update_ha_state() @property def device_class(self): if self._type == 'motion': return DEVICE_CLASS_MOTION elif self._type == 'cover': return DEVICE_CLASS_OPENING elif self._type == 'battery': return DEVICE_CLASS_BATTERY else: return None