mirror of
https://github.com/home-assistant/core.git
synced 2025-06-25 01:21:51 +02:00
Add initial support fo HomematicIP components (#12761)
* Add initial support fo HomematicIP components * Fix module import * Update reqirments file as well * Added HomematicIP files * Update to homematicip * Code cleanup based on highligted issues * Update of reqiremnets file as well * Fix dispatcher usage * Rename homematicip to homematicip_cloud
This commit is contained in:
committed by
Paulus Schoutsen
parent
8ed3024026
commit
b45dad507a
@ -109,6 +109,9 @@ omit =
|
||||
homeassistant/components/homematic/__init__.py
|
||||
homeassistant/components/*/homematic.py
|
||||
|
||||
homeassistant/components/homematicip_cloud.py
|
||||
homeassistant/components/*/homematicip_cloud.py
|
||||
|
||||
homeassistant/components/ihc/*
|
||||
homeassistant/components/*/ihc.py
|
||||
|
||||
|
170
homeassistant/components/homematicip_cloud.py
Normal file
170
homeassistant/components/homematicip_cloud.py
Normal file
@ -0,0 +1,170 @@
|
||||
"""
|
||||
Support for HomematicIP components.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/homematicip/
|
||||
"""
|
||||
|
||||
import logging
|
||||
from socket import timeout
|
||||
|
||||
import voluptuous as vol
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import (dispatcher_send,
|
||||
async_dispatcher_connect)
|
||||
from homeassistant.helpers.discovery import load_platform
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['homematicip==0.8']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'homematicip_cloud'
|
||||
|
||||
CONF_NAME = 'name'
|
||||
CONF_ACCESSPOINT = 'accesspoint'
|
||||
CONF_AUTHTOKEN = 'authtoken'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Optional(DOMAIN): [vol.Schema({
|
||||
vol.Optional(CONF_NAME, default=''): cv.string,
|
||||
vol.Required(CONF_ACCESSPOINT): cv.string,
|
||||
vol.Required(CONF_AUTHTOKEN): cv.string,
|
||||
})],
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
EVENT_HOME_CHANGED = 'homematicip_home_changed'
|
||||
EVENT_DEVICE_CHANGED = 'homematicip_device_changed'
|
||||
EVENT_GROUP_CHANGED = 'homematicip_group_changed'
|
||||
EVENT_SECURITY_CHANGED = 'homematicip_security_changed'
|
||||
EVENT_JOURNAL_CHANGED = 'homematicip_journal_changed'
|
||||
|
||||
ATTR_HOME_ID = 'home_id'
|
||||
ATTR_HOME_LABEL = 'home_label'
|
||||
ATTR_DEVICE_ID = 'device_id'
|
||||
ATTR_DEVICE_LABEL = 'device_label'
|
||||
ATTR_STATUS_UPDATE = 'status_update'
|
||||
ATTR_FIRMWARE_STATE = 'firmware_state'
|
||||
ATTR_LOW_BATTERY = 'low_battery'
|
||||
ATTR_SABOTAGE = 'sabotage'
|
||||
ATTR_RSSI = 'rssi'
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the HomematicIP component."""
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
from homematicip.home import Home
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
homes = hass.data[DOMAIN]
|
||||
accesspoints = config.get(DOMAIN, [])
|
||||
|
||||
def _update_event(events):
|
||||
"""Handle incoming HomeMaticIP events."""
|
||||
for event in events:
|
||||
etype = event['eventType']
|
||||
edata = event['data']
|
||||
if etype == 'DEVICE_CHANGED':
|
||||
dispatcher_send(hass, EVENT_DEVICE_CHANGED, edata.id)
|
||||
elif etype == 'GROUP_CHANGED':
|
||||
dispatcher_send(hass, EVENT_GROUP_CHANGED, edata.id)
|
||||
elif etype == 'HOME_CHANGED':
|
||||
dispatcher_send(hass, EVENT_HOME_CHANGED, edata.id)
|
||||
elif etype == 'JOURNAL_CHANGED':
|
||||
dispatcher_send(hass, EVENT_SECURITY_CHANGED, edata.id)
|
||||
return True
|
||||
|
||||
for device in accesspoints:
|
||||
name = device.get(CONF_NAME)
|
||||
accesspoint = device.get(CONF_ACCESSPOINT)
|
||||
authtoken = device.get(CONF_AUTHTOKEN)
|
||||
|
||||
home = Home()
|
||||
if name.lower() == 'none':
|
||||
name = ''
|
||||
home.label = name
|
||||
try:
|
||||
home.set_auth_token(authtoken)
|
||||
home.init(accesspoint)
|
||||
if home.get_current_state():
|
||||
_LOGGER.info("Connection to HMIP established")
|
||||
else:
|
||||
_LOGGER.warning("Connection to HMIP could not be established")
|
||||
return False
|
||||
except timeout:
|
||||
_LOGGER.warning("Connection to HMIP could not be established")
|
||||
return False
|
||||
homes[home.id] = home
|
||||
home.onEvent += _update_event
|
||||
home.enable_events()
|
||||
_LOGGER.info('HUB name: %s, id: %s', home.label, home.id)
|
||||
|
||||
for component in ['sensor']:
|
||||
load_platform(hass, component, DOMAIN,
|
||||
{'homeid': home.id}, config)
|
||||
return True
|
||||
|
||||
|
||||
class HomematicipGenericDevice(Entity):
|
||||
"""Representation of an HomematicIP generic device."""
|
||||
|
||||
def __init__(self, hass, home, device, signal=None):
|
||||
"""Initialize the generic device."""
|
||||
self.hass = hass
|
||||
self._home = home
|
||||
self._device = device
|
||||
async_dispatcher_connect(
|
||||
self.hass, EVENT_DEVICE_CHANGED, self._device_changed)
|
||||
|
||||
@callback
|
||||
def _device_changed(self, deviceid):
|
||||
"""Handle device state changes."""
|
||||
if deviceid is None or deviceid == self._device.id:
|
||||
_LOGGER.debug('Event device %s', self._device.label)
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
def _name(self, addon=''):
|
||||
"""Return the name of the device."""
|
||||
name = ''
|
||||
if self._home.label != '':
|
||||
name += self._home.label + ' '
|
||||
name += self._device.label
|
||||
if addon != '':
|
||||
name += ' ' + addon
|
||||
return name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the generic device."""
|
||||
return self._name()
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""No polling needed."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Device available."""
|
||||
return not self._device.unreach
|
||||
|
||||
def _generic_state_attributes(self):
|
||||
"""Return the state attributes of the generic device."""
|
||||
laststatus = ''
|
||||
if self._device.lastStatusUpdate is not None:
|
||||
laststatus = self._device.lastStatusUpdate.isoformat()
|
||||
return {
|
||||
ATTR_HOME_LABEL: self._home.label,
|
||||
ATTR_DEVICE_LABEL: self._device.label,
|
||||
ATTR_HOME_ID: self._device.homeId,
|
||||
ATTR_DEVICE_ID: self._device.id.lower(),
|
||||
ATTR_STATUS_UPDATE: laststatus,
|
||||
ATTR_FIRMWARE_STATE: self._device.updateState.lower(),
|
||||
ATTR_LOW_BATTERY: self._device.lowBat,
|
||||
ATTR_RSSI: self._device.rssiDeviceValue,
|
||||
}
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the generic device."""
|
||||
return self._generic_state_attributes()
|
258
homeassistant/components/sensor/homematicip_cloud.py
Normal file
258
homeassistant/components/sensor/homematicip_cloud.py
Normal file
@ -0,0 +1,258 @@
|
||||
"""
|
||||
Support for HomematicIP sensors.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/homematicip/
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.dispatcher import dispatcher_connect
|
||||
from homeassistant.components.homematicip_cloud import (
|
||||
HomematicipGenericDevice, DOMAIN, EVENT_HOME_CHANGED,
|
||||
ATTR_HOME_LABEL, ATTR_HOME_ID, ATTR_LOW_BATTERY, ATTR_RSSI)
|
||||
from homeassistant.const import TEMP_CELSIUS, STATE_OK
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['homematicip_cloud']
|
||||
|
||||
ATTR_VALVE_STATE = 'valve_state'
|
||||
ATTR_VALVE_POSITION = 'valve_position'
|
||||
ATTR_TEMPERATURE_OFFSET = 'temperature_offset'
|
||||
|
||||
HMIP_UPTODATE = 'up_to_date'
|
||||
HMIP_VALVE_DONE = 'adaption_done'
|
||||
HMIP_SABOTAGE = 'sabotage'
|
||||
|
||||
STATE_LOW_BATTERY = 'low_battery'
|
||||
STATE_SABOTAGE = 'sabotage'
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the HomematicIP sensors devices."""
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
from homematicip.device import (
|
||||
HeatingThermostat, TemperatureHumiditySensorWithoutDisplay,
|
||||
TemperatureHumiditySensorDisplay)
|
||||
|
||||
_LOGGER.info('Setting up HomeMaticIP accespoint & generic devices')
|
||||
homeid = discovery_info['homeid']
|
||||
home = hass.data[DOMAIN][homeid]
|
||||
devices = [HomematicipAccesspoint(hass, home)]
|
||||
if home.devices is None:
|
||||
return
|
||||
for device in home.devices:
|
||||
devices.append(HomematicipDeviceStatus(hass, home, device))
|
||||
if isinstance(device, HeatingThermostat):
|
||||
devices.append(HomematicipHeatingThermostat(hass, home, device))
|
||||
if isinstance(device, TemperatureHumiditySensorWithoutDisplay):
|
||||
devices.append(HomematicipSensorThermometer(hass, home, device))
|
||||
devices.append(HomematicipSensorHumidity(hass, home, device))
|
||||
if isinstance(device, TemperatureHumiditySensorDisplay):
|
||||
devices.append(HomematicipSensorThermometer(hass, home, device))
|
||||
devices.append(HomematicipSensorHumidity(hass, home, device))
|
||||
add_devices(devices)
|
||||
|
||||
|
||||
class HomematicipAccesspoint(Entity):
|
||||
"""Representation of an HomeMaticIP access point."""
|
||||
|
||||
def __init__(self, hass, home):
|
||||
"""Initialize the access point sensor."""
|
||||
self.hass = hass
|
||||
self._home = home
|
||||
dispatcher_connect(
|
||||
self.hass, EVENT_HOME_CHANGED, self._home_changed)
|
||||
_LOGGER.debug('Setting up access point %s', home.label)
|
||||
|
||||
@callback
|
||||
def _home_changed(self, deviceid):
|
||||
"""Handle device state changes."""
|
||||
if deviceid is None or deviceid == self._home.id:
|
||||
_LOGGER.debug('Event access point %s', self._home.label)
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the access point device."""
|
||||
if self._home.label == '':
|
||||
return 'Access Point Status'
|
||||
return '{} Access Point Status'.format(self._home.label)
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon of the access point device."""
|
||||
return 'mdi:access-point-network'
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the access point."""
|
||||
return self._home.dutyCycle
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Device available."""
|
||||
return self._home.connected
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the access point."""
|
||||
return {
|
||||
ATTR_HOME_LABEL: self._home.label,
|
||||
ATTR_HOME_ID: self._home.id,
|
||||
}
|
||||
|
||||
|
||||
class HomematicipDeviceStatus(HomematicipGenericDevice):
|
||||
"""Representation of an HomematicIP device status."""
|
||||
|
||||
def __init__(self, hass, home, device, signal=None):
|
||||
"""Initialize the device."""
|
||||
super().__init__(hass, home, device)
|
||||
_LOGGER.debug('Setting up sensor device status: %s', device.label)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name('Status')
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon of the status device."""
|
||||
if (hasattr(self._device, 'sabotage') and
|
||||
self._device.sabotage == HMIP_SABOTAGE):
|
||||
return 'mdi:alert'
|
||||
elif self._device.lowBat:
|
||||
return 'mdi:battery-outline'
|
||||
elif self._device.updateState.lower() != HMIP_UPTODATE:
|
||||
return 'mdi:refresh'
|
||||
return 'mdi:check'
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the generic device."""
|
||||
if (hasattr(self._device, 'sabotage') and
|
||||
self._device.sabotage == HMIP_SABOTAGE):
|
||||
return STATE_SABOTAGE
|
||||
elif self._device.lowBat:
|
||||
return STATE_LOW_BATTERY
|
||||
elif self._device.updateState.lower() != HMIP_UPTODATE:
|
||||
return self._device.updateState.lower()
|
||||
return STATE_OK
|
||||
|
||||
|
||||
class HomematicipHeatingThermostat(HomematicipGenericDevice):
|
||||
"""MomematicIP heating thermostat representation."""
|
||||
|
||||
def __init__(self, hass, home, device):
|
||||
""""Initialize heating thermostat."""
|
||||
super().__init__(hass, home, device)
|
||||
_LOGGER.debug('Setting up heating thermostat device: %s', device.label)
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon."""
|
||||
if self._device.valveState.lower() != HMIP_VALVE_DONE:
|
||||
return 'mdi:alert'
|
||||
return 'mdi:radiator'
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the radiator valve."""
|
||||
if self._device.valveState.lower() != HMIP_VALVE_DONE:
|
||||
return self._device.valveState.lower()
|
||||
return round(self._device.valvePosition*100)
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit this state is expressed in."""
|
||||
return '%'
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
ATTR_VALVE_STATE: self._device.valveState.lower(),
|
||||
ATTR_TEMPERATURE_OFFSET: self._device.temperatureOffset,
|
||||
ATTR_LOW_BATTERY: self._device.lowBat,
|
||||
ATTR_RSSI: self._device.rssiDeviceValue
|
||||
}
|
||||
|
||||
|
||||
class HomematicipSensorHumidity(HomematicipGenericDevice):
|
||||
"""MomematicIP thermometer device."""
|
||||
|
||||
def __init__(self, hass, home, device):
|
||||
""""Initialize the thermometer device."""
|
||||
super().__init__(hass, home, device)
|
||||
_LOGGER.debug('Setting up humidity device: %s',
|
||||
device.label)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name('Humidity')
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon."""
|
||||
return 'mdi:water'
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state."""
|
||||
return self._device.humidity
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit this state is expressed in."""
|
||||
return '%'
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
ATTR_LOW_BATTERY: self._device.lowBat,
|
||||
ATTR_RSSI: self._device.rssiDeviceValue,
|
||||
}
|
||||
|
||||
|
||||
class HomematicipSensorThermometer(HomematicipGenericDevice):
|
||||
"""MomematicIP thermometer device."""
|
||||
|
||||
def __init__(self, hass, home, device):
|
||||
""""Initialize the thermometer device."""
|
||||
super().__init__(hass, home, device)
|
||||
_LOGGER.debug('Setting up thermometer device: %s', device.label)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name('Temperature')
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon."""
|
||||
return 'mdi:thermometer'
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state."""
|
||||
return self._device.actualTemperature
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit this state is expressed in."""
|
||||
return TEMP_CELSIUS
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
ATTR_TEMPERATURE_OFFSET: self._device.temperatureOffset,
|
||||
ATTR_LOW_BATTERY: self._device.lowBat,
|
||||
ATTR_RSSI: self._device.rssiDeviceValue,
|
||||
}
|
@ -358,6 +358,9 @@ holidays==0.9.4
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20180316.0
|
||||
|
||||
# homeassistant.components.homematicip_cloud
|
||||
homematicip==0.8
|
||||
|
||||
# homeassistant.components.camera.onvif
|
||||
http://github.com/tgaugry/suds-passworddigest-py3/archive/86fc50e39b4d2b8997481967d6a7fe1c57118999.zip#suds-passworddigest-py3==0.1.2a
|
||||
|
||||
|
Reference in New Issue
Block a user