148 lines
5.2 KiB
Python
148 lines
5.2 KiB
Python
|
"""Support for lights from FHEM"""
|
||
|
|
||
|
import voluptuous as vol
|
||
|
import logging
|
||
|
|
||
|
from homeassistant.components.light import ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light
|
||
|
from homeassistant.const import CONF_NAME
|
||
|
import homeassistant.helpers.config_validation as cv
|
||
|
|
||
|
from . import DATA_FHEM
|
||
|
|
||
|
_LOGGER = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
CONF_FHEM_IDS = 'fhem_ids'
|
||
|
CONF_DIMMER = 'dimmer'
|
||
|
|
||
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||
|
vol.Required(CONF_FHEM_IDS): vol.All(cv.ensure_list, [cv.string]),
|
||
|
vol.Optional(CONF_DIMMER, default=False): cv.boolean,
|
||
|
vol.Required(CONF_NAME): cv.string,
|
||
|
})
|
||
|
|
||
|
|
||
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||
|
"""Set up lights for KNX platform."""
|
||
|
connection = hass.data[DATA_FHEM]
|
||
|
#_LOGGER.error("FHEM platform config\n" + str(config))
|
||
|
|
||
|
light = FhemLight(connection, config[CONF_NAME], config[CONF_FHEM_IDS], dimmer=config[CONF_DIMMER])
|
||
|
for dev_id in config[CONF_FHEM_IDS]:
|
||
|
connection.devices[dev_id] = light
|
||
|
async_add_entities([light])
|
||
|
|
||
|
|
||
|
class FhemLight(Light):
|
||
|
|
||
|
def __init__(self, connection, name, ids, dimmer=False):
|
||
|
self._brightness = None
|
||
|
self.connection = connection
|
||
|
self._dimmer = dimmer
|
||
|
self._ids = ids
|
||
|
self._name = name
|
||
|
self._available = True
|
||
|
|
||
|
@property
|
||
|
def should_poll(self):
|
||
|
"""No polling needed."""
|
||
|
return False
|
||
|
|
||
|
@property
|
||
|
def brightness(self):
|
||
|
return self._brightness
|
||
|
|
||
|
@property
|
||
|
def is_on(self):
|
||
|
"""Return true if light is on."""
|
||
|
if self._brightness is not None:
|
||
|
return self._brightness > 0
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
"""Return the name of the KNX device."""
|
||
|
return self._name
|
||
|
|
||
|
@property
|
||
|
def available(self) -> bool:
|
||
|
return self._available and self.connection.connected
|
||
|
|
||
|
@property
|
||
|
def supported_features(self):
|
||
|
"""Flag supported features."""
|
||
|
flags = 0
|
||
|
if self._dimmer:
|
||
|
flags |= SUPPORT_BRIGHTNESS
|
||
|
return flags
|
||
|
|
||
|
async def async_turn_on(self, **kwargs):
|
||
|
brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness)
|
||
|
if brightness is None:
|
||
|
brightness = 255
|
||
|
|
||
|
if self._dimmer:
|
||
|
self.connection.fhem_set(self._ids[0], brightness / 255 * 100)
|
||
|
else:
|
||
|
self.connection.fhem_set(self._ids[0], 'on')
|
||
|
|
||
|
async def async_turn_off(self, **kwargs):
|
||
|
self.connection.fhem_set(self._ids[0], 'off')
|
||
|
|
||
|
async def line_received(self, line):
|
||
|
_LOGGER.info("FHEM line received (device): " + self.name + ": " + line)
|
||
|
if line.startswith('dim:'):
|
||
|
self._available = True
|
||
|
_, new_dim_state, new_level = line.split(':')
|
||
|
new_level = new_level.strip().lower()
|
||
|
new_dim_state = new_dim_state.strip().lower()
|
||
|
assert new_dim_state == 'stop' or new_dim_state == 'up' or new_dim_state == 'down'
|
||
|
if new_dim_state == 'stop':
|
||
|
if new_level == 'on':
|
||
|
self._brightness = 255
|
||
|
elif new_level == 'off':
|
||
|
self._brightness = 0
|
||
|
else:
|
||
|
new_level = int(float(new_level)) # first convert from string to floating point then truncate
|
||
|
assert 0 <= new_level <= 100
|
||
|
self._brightness = int(new_level / 100 * 255)
|
||
|
await self.async_update_ha_state()
|
||
|
elif line.startswith('level:') and not self._dimmer:
|
||
|
self._available = True
|
||
|
_, new_level = line.split(':')
|
||
|
new_level = new_level.strip().lower()
|
||
|
if new_level == 'on':
|
||
|
self._brightness = 255
|
||
|
elif new_level == 'off':
|
||
|
self._brightness = 0
|
||
|
else:
|
||
|
try:
|
||
|
new_level = int(float(new_level)) # first convert from string to floating point then truncate
|
||
|
assert 0 <= new_level <= 100
|
||
|
self._brightness = int(new_level / 100 * 255)
|
||
|
except ValueError:
|
||
|
pass
|
||
|
await self.async_update_ha_state()
|
||
|
elif line.startswith('overheat'):
|
||
|
overheat = line.split(':')[1]
|
||
|
overheat = overheat.strip().lower()
|
||
|
assert overheat == 'on' or overheat == 'off'
|
||
|
if overheat == 'on':
|
||
|
self.hass.components.persistent_notification.async_create(
|
||
|
"FHEM: Light overheated: <br>"
|
||
|
"<b>{0}</b>".format(self.entity_id),
|
||
|
title="Light overheat")
|
||
|
elif line.startswith('overload'):
|
||
|
overload = line.split(':')[1]
|
||
|
overload = overload.strip().lower()
|
||
|
assert overload == 'on' or overload == 'off'
|
||
|
if overload == 'on':
|
||
|
self.hass.components.persistent_notification.async_create(
|
||
|
"FHEM: Light overloaded: <br>"
|
||
|
"<b>{0}</b>".format(self.entity_id),
|
||
|
title="Light overloaded")
|
||
|
elif line.startswith('ResndFail') or line.startswith('MISSING ACK'):
|
||
|
self._available = False
|
||
|
await self.async_update_ha_state()
|