Release 0.18 #1854

0.18
This commit is contained in:
Paulus Schoutsen 2016-04-20 18:43:54 -07:00
commit 8f18cb34d6
171 changed files with 2937 additions and 1020 deletions

View file

@ -5,18 +5,18 @@ omit =
homeassistant/__main__.py
# omit pieces of code that rely on external devices being present
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
homeassistant/components/arduino.py
homeassistant/components/*/arduino.py
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
homeassistant/components/bloomsky.py
homeassistant/components/*/bloomsky.py
homeassistant/components/ecobee.py
homeassistant/components/*/ecobee.py
homeassistant/components/insteon_hub.py
homeassistant/components/*/insteon_hub.py
@ -26,33 +26,6 @@ omit =
homeassistant/components/modbus.py
homeassistant/components/*/modbus.py
homeassistant/components/tellstick.py
homeassistant/components/*/tellstick.py
homeassistant/components/tellduslive.py
homeassistant/components/*/tellduslive.py
homeassistant/components/vera.py
homeassistant/components/*/vera.py
homeassistant/components/ecobee.py
homeassistant/components/*/ecobee.py
homeassistant/components/verisure.py
homeassistant/components/*/verisure.py
homeassistant/components/wemo.py
homeassistant/components/*/wemo.py
homeassistant/components/wink.py
homeassistant/components/*/wink.py
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zwave.py
homeassistant/components/*/zwave.py
homeassistant/components/mysensors.py
homeassistant/components/*/mysensors.py
@ -65,6 +38,36 @@ omit =
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py
homeassistant/components/tellduslive.py
homeassistant/components/*/tellduslive.py
homeassistant/components/tellstick.py
homeassistant/components/*/tellstick.py
homeassistant/components/*/thinkingcleaner.py
homeassistant/components/vera.py
homeassistant/components/*/vera.py
homeassistant/components/verisure.py
homeassistant/components/*/verisure.py
homeassistant/components/*/webostv.py
homeassistant/components/wemo.py
homeassistant/components/*/wemo.py
homeassistant/components/wink.py
homeassistant/components/*/wink.py
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zwave.py
homeassistant/components/*/zwave.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/binary_sensor/arest.py
homeassistant/components/binary_sensor/rest.py
homeassistant/components/browser.py
@ -76,6 +79,7 @@ omit =
homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
homeassistant/components/device_tracker/bluetooth_tracker.py
homeassistant/components/device_tracker/ddwrt.py
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/icloud.py
@ -89,6 +93,7 @@ omit =
homeassistant/components/device_tracker/ubus.py
homeassistant/components/discovery.py
homeassistant/components/downloader.py
homeassistant/components/feedreader.py
homeassistant/components/garage_door/wink.py
homeassistant/components/ifttt.py
homeassistant/components/keyboard.py
@ -103,17 +108,17 @@ omit =
homeassistant/components/media_player/itunes.py
homeassistant/components/media_player/kodi.py
homeassistant/components/media_player/mpd.py
homeassistant/components/media_player/onkyo.py
homeassistant/components/media_player/panasonic_viera.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/samsungtv.py
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/onkyo.py
homeassistant/components/media_player/panasonic_viera.py
homeassistant/components/media_player/yamaha.py
homeassistant/components/notify/free_mobile.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/notify/gntp.py
homeassistant/components/notify/googlevoice.py
homeassistant/components/notify/instapush.py
homeassistant/components/notify/message_bird.py
homeassistant/components/notify/nma.py
@ -140,10 +145,10 @@ omit =
homeassistant/components/sensor/forecast.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/gtfs.py
homeassistant/components/sensor/netatmo.py
homeassistant/components/sensor/nzbget.py
homeassistant/components/sensor/loopenergy.py
homeassistant/components/sensor/netatmo.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/nzbget.py
homeassistant/components/sensor/onewire.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/rest.py
@ -169,10 +174,13 @@ omit =
homeassistant/components/switch/rest.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
homeassistant/components/thermostat/eq3btsmart.py
homeassistant/components/thermostat/heatmiser.py
homeassistant/components/thermostat/homematic.py
homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.py
homeassistant/components/upnp.py
homeassistant/components/zeroconf.py
[report]

View file

@ -23,6 +23,6 @@ If the code does not interact with devices:
[fork]: http://stackoverflow.com/a/7244456
[squash]: https://github.com/ginatrapani/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit
[ex-requir]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16
[ex-import]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51
[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16
[ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51

1
.gitignore vendored
View file

@ -8,6 +8,7 @@ config/custom_components/*
!config/custom_components/hello_world.py
!config/custom_components/mqtt_example.py
tests/config/deps
tests/config/home-assistant.log
# Hide sublime text stuff

2
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "homeassistant/components/frontend/www_static/home-assistant-polymer"]
path = homeassistant/components/frontend/www_static/home-assistant-polymer
url = https://github.com/balloob/home-assistant-polymer.git
url = https://github.com/home-assistant/home-assistant-polymer.git

View file

@ -4,10 +4,10 @@ Everybody is invited and welcome to contribute to Home Assistant. There is a lot
The process is straight-forward.
- Fork the Home Assistant [git repository](https://github.com/balloob/home-assistant).
- Fork the Home Assistant [git repository](https://github.com/home-assistant/home-assistant).
- Write the code for your device, notification service, sensor, or IoT thing.
- Ensure tests work.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
- Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant.
Still interested? Then you should read the next sections and get more details.
@ -20,20 +20,20 @@ After you finish adding support for your device:
- Check that all dependencies are included via the `REQUIREMENTS` variable in your platform/component and only imported inside functions that use them.
- Add any new dependencies to `requirements_all.txt` if needed. Use `script/gen_requirements_all.py`.
- Update the `.coveragerc` file to exclude your platform if there are no tests available or your new code uses a 3rd party library for communication with the device/service/sensor.
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). It's OK to just add a docstring with configuration details (sample entry for `configuration.yaml` file and alike) to the file header as a start. Visit the [website documentation](https://home-assistant.io/developers/website/) for further information on contributing to [home-assistant.io](https://github.com/balloob/home-assistant.io).
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). It's OK to just add a docstring with configuration details (sample entry for `configuration.yaml` file and alike) to the file header as a start. Visit the [website documentation](https://home-assistant.io/developers/website/) for further information on contributing to [home-assistant.io](https://github.com/home-assistant/home-assistant.io).
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `tox` or `script/lint`.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
- Check for comments and suggestions on your Pull Request and keep an eye on the [CI output](https://travis-ci.org/balloob/home-assistant/).
- Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant.
- Check for comments and suggestions on your Pull Request and keep an eye on the [CI output](https://travis-ci.org/home-assistant/home-assistant/).
If you add a platform for an existing component, there is usually no need for updating the frontend. Only if you've added a new component that should show up in the frontend, there are more steps needed:
- Update the file [`home-assistant-icons.html`](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/frontend/www_static/polymer/resources/home-assistant-icons.html) with an icon for your domain ([pick one from this list](https://www.polymer-project.org/1.0/components/core-elements/demo.html#core-icon)).
- Update the file [`home-assistant-icons.html`](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/frontend/www_static/polymer/resources/home-assistant-icons.html) with an icon for your domain ([pick one from this list](https://www.polymer-project.org/1.0/components/core-elements/demo.html#core-icon)).
- Update the demo component with two states that it provides.
- Add your component to `home-assistant.conf.example`.
Since you've updated `home-assistant-icons.html`, you've made changes to the frontend:
- Run `build_frontend`. This will build a new version of the frontend. Make sure you add the changed files `frontend.py` and `frontend.html` to the commit.
- Run `script/build_frontend`. This will build a new version of the frontend. Make sure you add the changed files `frontend.py` and `frontend.html` to the commit.
### Setting states
@ -46,11 +46,11 @@ A state can have several attributes that will help the frontend in displaying yo
- `unit_of_measurement`: this will be appended to the state in the interface
- `hidden`: This is a suggestion to the frontend on if the state should be hidden
These attributes are defined in [homeassistant.components](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/__init__.py#L25).
These attributes are defined in [homeassistant.components](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/__init__.py#L25).
### Proper Visibility Handling
Generally, when creating a new entity for Home Assistant you will want it to be a class that inherits the [homeassistant.helpers.entity.Entity](https://github.com/balloob/home-assistant/blob/master/homeassistant/helpers/entity.py) class. If this is done, visibility will be handled for you.
Generally, when creating a new entity for Home Assistant you will want it to be a class that inherits the [homeassistant.helpers.entity.Entity](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/helpers/entity.py) class. If this is done, visibility will be handled for you.
You can set a suggestion for your entity's visibility by setting the hidden property by doing something similar to the following.
```python

View file

@ -8,9 +8,9 @@ WORKDIR /usr/src/app
RUN pip3 install --no-cache-dir colorlog cython
# For the nmap tracker
# For the nmap tracker, bluetooth tracker, Z-Wave
RUN apt-get update && \
apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo && \
apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo libglib2.0-dev && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY script/build_python_openzwave script/build_python_openzwave

View file

@ -1,4 +1,4 @@
Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/balloob/home-assistant|
Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/home-assistant/home-assistant|
===========================================================================================================
Home Assistant is a home automation platform running on Python 3. The
@ -88,11 +88,11 @@ If you run into issues while using Home Assistant or during development
of a component, check the `Home Assistant help
section <https://home-assistant.io/help/>`__ how to reach us.
.. |Build Status| image:: https://travis-ci.org/balloob/home-assistant.svg?branch=master
:target: https://travis-ci.org/balloob/home-assistant
.. |Coverage Status| image:: https://img.shields.io/coveralls/balloob/home-assistant.svg
:target: https://coveralls.io/r/balloob/home-assistant?branch=master
.. |Join the chat at https://gitter.im/balloob/home-assistant| image:: https://badges.gitter.im/Join%20Chat.svg
:target: https://gitter.im/balloob/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |screenshot-states| image:: https://raw.github.com/balloob/home-assistant/master/docs/screenshots.png
.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=master
:target: https://travis-ci.org/home-assistant/home-assistant
.. |Coverage Status| image:: https://img.shields.io/coveralls/home-assistant/home-assistant.svg
:target: https://coveralls.io/r/home-assistant/home-assistant?branch=master
.. |Join the chat at https://gitter.im/home-assistant/home-assistant| image:: https://badges.gitter.im/Join%20Chat.svg
:target: https://gitter.im/home-assistant/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |screenshot-states| image:: https://raw.github.com/home-assistant/home-assistant/master/docs/screenshots.png
:target: https://home-assistant.io/demo/

View file

@ -7,7 +7,7 @@ homeassistant:
latitude: 32.87336
longitude: 117.22743
# C for Celcius, F for Fahrenheit
# C for Celsius, F for Fahrenheit
temperature_unit: C
# Pick yours from here:

View file

@ -14,7 +14,7 @@ To use the mqtt_example component you will need to add the following to your
configuration.yaml file.
mqtt_example:
topic: home-assistant/mqtt_example
topic: "home-assistant/mqtt_example"
"""
import homeassistant.loader as loader
@ -29,7 +29,7 @@ DEFAULT_TOPIC = 'home-assistant/mqtt_example'
def setup(hass, config):
"""Setup the MQTT example component."""
"""Setup the MQTT example component."""
mqtt = loader.get_component('mqtt')
topic = config[DOMAIN].get('topic', DEFAULT_TOPIC)
entity_id = 'mqtt_example.last_message'

View file

@ -31,7 +31,7 @@ def validate_python():
def ensure_config_path(config_dir):
"""Validate the configuration directory."""
import homeassistant.config as config_util
lib_dir = os.path.join(config_dir, 'lib')
lib_dir = os.path.join(config_dir, 'deps')
# Test if configuration directory exists
if not os.path.isdir(config_dir):

View file

@ -21,7 +21,7 @@ import homeassistant.util.package as pkg_util
from homeassistant.const import (
CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME,
CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED,
TEMP_CELCIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__)
TEMP_CELSIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
event_decorators, service, config_per_platform, extract_domain_configs)
@ -65,7 +65,7 @@ def _handle_requirements(hass, component, name):
return True
for req in component.REQUIREMENTS:
if not pkg_util.install_package(req, target=hass.config.path('lib')):
if not pkg_util.install_package(req, target=hass.config.path('deps')):
_LOGGER.error('Not initializing %s because could not install '
'dependency %s', name, req)
return False
@ -211,7 +211,7 @@ def prepare_setup_platform(hass, config, domain, platform_name):
def mount_local_lib_path(config_dir):
"""Add local library to Python Path."""
sys.path.insert(0, os.path.join(config_dir, 'lib'))
sys.path.insert(0, os.path.join(config_dir, 'deps'))
# pylint: disable=too-many-branches, too-many-statements, too-many-arguments
@ -372,10 +372,16 @@ def process_ha_config_upgrade(hass):
_LOGGER.info('Upgrading config directory from %s to %s', conf_version,
__version__)
# This was where dependencies were installed before v0.18
# Probably should keep this around until ~v0.20.
lib_path = hass.config.path('lib')
if os.path.isdir(lib_path):
shutil.rmtree(lib_path)
lib_path = hass.config.path('deps')
if os.path.isdir(lib_path):
shutil.rmtree(lib_path)
with open(version_path, 'wt') as outp:
outp.write(__version__)
@ -434,7 +440,7 @@ def process_ha_core_config(hass, config):
if info.use_fahrenheit:
hac.temperature_unit = TEMP_FAHRENHEIT
else:
hac.temperature_unit = TEMP_CELCIUS
hac.temperature_unit = TEMP_CELSIUS
if hac.location_name is None:
hac.location_name = info.city

View file

@ -7,12 +7,15 @@ https://home-assistant.io/components/alarm_control_panel/
import logging
import os
import voluptuous as vol
from homeassistant.components import verisure
from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@ -38,6 +41,11 @@ ATTR_TO_PROPERTY = [
ATTR_CODE_FORMAT
]
ALARM_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_CODE): cv.string,
})
def setup(hass, config):
"""Track states and offer events for sensors."""
@ -51,10 +59,7 @@ def setup(hass, config):
"""Map services to methods on Alarm."""
target_alarms = component.extract_from_service(service)
if ATTR_CODE not in service.data:
code = None
else:
code = service.data[ATTR_CODE]
code = service.data.get(ATTR_CODE)
method = SERVICE_TO_METHOD[service.service]
@ -68,8 +73,8 @@ def setup(hass, config):
for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, alarm_service_handler,
descriptions.get(service))
descriptions.get(service),
schema=ALARM_SERVICE_SCHEMA)
return True

View file

@ -51,8 +51,6 @@ def _platform_validator(method, schema):
if not hasattr(platform, schema):
return config
print('validating config', method, config)
return getattr(platform, schema)(config)
return validator

View file

@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__)
def trigger(hass, config, action):
"""Listen for state changes based on configuration."""
if CONF_AFTER in config:
after = dt_util.parse_time_str(config[CONF_AFTER])
after = dt_util.parse_time(config[CONF_AFTER])
if after is None:
_error_time(config[CONF_AFTER], CONF_AFTER)
return False
@ -62,13 +62,13 @@ def if_action(hass, config):
return None
if before is not None:
before = dt_util.parse_time_str(before)
before = dt_util.parse_time(before)
if before is None:
_error_time(before, CONF_BEFORE)
return None
if after is not None:
after = dt_util.parse_time_str(after)
after = dt_util.parse_time(after)
if after is None:
_error_time(after, CONF_AFTER)
return None

View file

@ -6,10 +6,9 @@ https://home-assistant.io/components/binary_sensor.mysensors/
"""
import logging
from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, SENSOR_CLASSES)
from homeassistant.components.binary_sensor import (SENSOR_CLASSES,
BinarySensorDevice)
from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
from homeassistant.loader import get_component
_LOGGER = logging.getLogger(__name__)
@ -101,8 +100,13 @@ class MySensorsBinarySensor(BinarySensorDevice):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
address = getattr(self.gateway, 'server_address', None)
if address:
device = '{}:{}'.format(address[0], address[1])
else:
device = self.gateway.port
attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,

View file

@ -4,12 +4,14 @@ Support for Nest Thermostat Binary Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.nest/
"""
import logging
import socket
import voluptuous as vol
import homeassistant.components.nest as nest
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.nest import NestSensor
from homeassistant.const import (
CONF_PLATFORM, CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS
)
DEPENDENCIES = ['nest']
BINARY_TYPES = ['fan',
@ -23,25 +25,19 @@ BINARY_TYPES = ['fan',
'hvac_emer_heat_state',
'online']
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): nest.DOMAIN,
vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Required(CONF_MONITORED_CONDITIONS): [vol.In(BINARY_TYPES)],
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Nest binary sensors."""
logger = logging.getLogger(__name__)
try:
for structure in nest.NEST.structures:
for device in structure.devices:
for variable in config['monitored_conditions']:
if variable in BINARY_TYPES:
add_devices([NestBinarySensor(structure,
device,
variable)])
else:
logger.error('Nest sensor type: "%s" does not exist',
variable)
except socket.error:
logger.error(
"Connection error logging into the nest web service."
)
for structure, device in nest.devices():
add_devices([NestBinarySensor(structure, device, variable)
for variable in config[CONF_MONITORED_CONDITIONS]])
class NestBinarySensor(NestSensor, BinarySensorDevice):

View file

@ -49,8 +49,7 @@ class VeraBinarySensor(VeraDevice, BinarySensorDevice):
last_tripped = self.vera_device.last_trip
if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time)
attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
else:
attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped

View file

@ -10,7 +10,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {

View file

@ -8,11 +8,7 @@ import logging
import datetime
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_point_in_time
from homeassistant.components.zwave import (
ATTR_NODE_ID, ATTR_VALUE_ID,
COMMAND_CLASS_SENSOR_BINARY, NETWORK,
ZWaveDeviceEntity, get_config_value)
from homeassistant.components import zwave
from homeassistant.components.binary_sensor import (
DOMAIN,
BinarySensorDevice)
@ -36,11 +32,11 @@ DEVICE_MAPPINGS = {
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Z-Wave platform for sensors."""
if discovery_info is None or NETWORK is None:
if discovery_info is None or zwave.NETWORK is None:
return
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
value.set_change_verified(False)
# Make sure that we have values for the key before converting to int
@ -53,18 +49,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT:
# Default the multiplier to 4
re_arm_multiplier = (get_config_value(value.node, 9) or 4)
re_arm_multiplier = (zwave.get_config_value(value.node,
9) or 4)
add_devices([
ZWaveTriggerSensor(value, "motion",
hass, re_arm_multiplier * 8)
])
return
if value.command_class == COMMAND_CLASS_SENSOR_BINARY:
if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY:
add_devices([ZWaveBinarySensor(value, None)])
class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
"""Representation of a binary sensor within Z-Wave."""
def __init__(self, value, sensor_class):
@ -74,7 +71,7 @@ class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)

View file

@ -4,10 +4,18 @@ Provides functionality to launch a web browser on the host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/browser/
"""
import voluptuous as vol
DOMAIN = "browser"
SERVICE_BROWSE_URL = "browse_url"
ATTR_URL = 'url'
ATTR_URL_DEFAULT = 'https://www.google.com'
SERVICE_BROWSE_URL_SCHEMA = vol.Schema({
vol.Required(ATTR_URL, default=ATTR_URL_DEFAULT): vol.Url,
})
def setup(hass, config):
"""Listen for browse_url events."""
@ -15,8 +23,7 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,
lambda service:
webbrowser.open(
service.data.get(
'url', 'https://www.google.com')))
webbrowser.open(service.data[ATTR_URL]),
schema=SERVICE_BROWSE_URL_SCHEMA)
return True

View file

@ -8,9 +8,12 @@ import logging
import re
import warnings
import voluptuous as vol
from homeassistant import core
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
import homeassistant.helpers.config_validation as cv
DOMAIN = "conversation"
@ -18,6 +21,10 @@ SERVICE_PROCESS = "process"
ATTR_TEXT = "text"
SERVICE_PROCESS_SCHEMA = vol.Schema({
vol.Required(ATTR_TEXT): vol.All(cv.string, vol.Lower),
})
REGEX_TURN_COMMAND = re.compile(r'turn (?P<name>(?: |\w)+) (?P<command>\w+)')
REQUIREMENTS = ['fuzzywuzzy==0.8.0']
@ -32,11 +39,7 @@ def setup(hass, config):
def process(service):
"""Parse text into commands."""
if ATTR_TEXT not in service.data:
logger.error("Received process service call without a text")
return
text = service.data[ATTR_TEXT].lower()
text = service.data[ATTR_TEXT]
match = REGEX_TURN_COMMAND.match(text)
if not match:
@ -67,6 +70,6 @@ def setup(hass, config):
logger.error(
'Got unsupported command %s from text %s', command, text)
hass.services.register(DOMAIN, SERVICE_PROCESS, process)
hass.services.register(DOMAIN, SERVICE_PROCESS, process,
schema=SERVICE_PROCESS_SCHEMA)
return True

View file

@ -94,7 +94,7 @@ def setup(hass, config):
yaml_path = hass.config.path(YAML_DEVICES)
conf = config.get(DOMAIN, {})
if isinstance(conf, list):
if isinstance(conf, list) and len(conf) > 0:
conf = conf[0]
consider_home = timedelta(
seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int,

View file

@ -0,0 +1,91 @@
"""Tracking for bluetooth devices."""
import logging
from datetime import timedelta
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.components.device_tracker import (
YAML_DEVICES,
CONF_TRACK_NEW,
CONF_SCAN_INTERVAL,
DEFAULT_SCAN_INTERVAL,
load_config,
)
import homeassistant.util as util
import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pybluez==0.22']
BT_PREFIX = 'BT_'
def setup_scanner(hass, config, see):
"""Setup the Bluetooth Scanner."""
# pylint: disable=import-error
import bluetooth
def see_device(device):
"""Mark a device as seen."""
see(mac=BT_PREFIX + device[0], host_name=device[1])
def discover_devices():
"""Discover bluetooth devices."""
result = bluetooth.discover_devices(duration=8,
lookup_names=True,
flush_cache=True,
lookup_class=False)
_LOGGER.debug("Bluetooth devices discovered = " + str(len(result)))
return result
yaml_path = hass.config.path(YAML_DEVICES)
devs_to_track = []
devs_donot_track = []
# Load all known devices.
# We just need the devices so set consider_home and home range
# to 0
for device in load_config(yaml_path, hass, 0, 0):
# check if device is a valid bluetooth device
if device.mac and device.mac[:3].upper() == BT_PREFIX:
if device.track:
devs_to_track.append(device.mac[3:])
else:
devs_donot_track.append(device.mac[3:])
# if track new devices is true discover new devices
# on startup.
track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
len(devs_to_track) == 0)
if track_new:
for dev in discover_devices():
if dev[0] not in devs_to_track and \
dev[0] not in devs_donot_track:
devs_to_track.append(dev[0])
see_device(dev)
if not devs_to_track:
_LOGGER.warning("No bluetooth devices to track!")
return False
interval = util.convert(config.get(CONF_SCAN_INTERVAL), int,
DEFAULT_SCAN_INTERVAL)
def update_bluetooth(now):
"""Lookup bluetooth device and update status."""
try:
for mac in devs_to_track:
_LOGGER.debug("Scanning " + mac)
result = bluetooth.lookup_name(mac, timeout=5)
if not result:
# Could not lookup device name
continue
see_device((mac, result))
except bluetooth.BluetoothError:
_LOGGER.exception('Error looking up bluetooth device!')
track_point_in_utc_time(hass, update_bluetooth,
now + timedelta(seconds=interval))
update_bluetooth(dt_util.utcnow())
return True

View file

@ -12,7 +12,9 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
REQUIREMENTS = ['fritzconnection==0.4.6']
REQUIREMENTS = ['https://github.com/deisi/fritzconnection/archive/'
'b5c14515e1c8e2652b06b6316a7f3913df942841.zip'
'#fritzconnection==0.4.6']
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)

View file

@ -9,14 +9,15 @@ import threading
from datetime import timedelta
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, \
CONF_PORT
from homeassistant.util import Throttle
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pynetgear==0.3.2']
REQUIREMENTS = ['pynetgear==0.3.3']
def get_scanner(hass, config):
@ -25,12 +26,13 @@ def get_scanner(hass, config):
host = info.get(CONF_HOST)
username = info.get(CONF_USERNAME)
password = info.get(CONF_PASSWORD)
port = info.get(CONF_PORT)
if password is not None and host is None:
_LOGGER.warning('Found username or password but no host')
return None
scanner = NetgearDeviceScanner(host, username, password)
scanner = NetgearDeviceScanner(host, username, password, port)
return scanner if scanner.success_init else None
@ -38,7 +40,7 @@ def get_scanner(hass, config):
class NetgearDeviceScanner(object):
"""Queries a Netgear wireless router using the SOAP-API."""
def __init__(self, host, username, password):
def __init__(self, host, username, password, port):
"""Initialize the scanner."""
import pynetgear
@ -49,8 +51,10 @@ class NetgearDeviceScanner(object):
self._api = pynetgear.Netgear()
elif username is None:
self._api = pynetgear.Netgear(password, host)
else:
elif port is None:
self._api = pynetgear.Netgear(password, host, username)
else:
self._api = pynetgear.Netgear(password, host, username, port)
_LOGGER.info("Logging in")

View file

@ -34,21 +34,33 @@ def setup_scanner(hass, config, see):
"""Setup an OwnTracks tracker."""
max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY)
def owntracks_location_update(topic, payload, qos):
"""MQTT message received."""
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typelocation
def validate_payload(payload, data_type):
"""Validate OwnTracks payload."""
try:
data = json.loads(payload)
except ValueError:
# If invalid JSON
_LOGGER.error(
'Unable to parse payload as JSON: %s', payload)
return
_LOGGER.error('Unable to parse payload as JSON: %s', payload)
return None
if not isinstance(data, dict) or data.get('_type') != data_type:
_LOGGER.debug('Skipping %s update for following data '
'because of missing or malformatted data: %s',
data_type, data)
return None
if max_gps_accuracy is not None and \
convert(data.get('acc'), float, 0.0) > max_gps_accuracy:
_LOGGER.debug('Skipping %s update because expected GPS '
'accuracy %s is not met: %s',
data_type, max_gps_accuracy, data)
return None
return data
if (not isinstance(data, dict) or data.get('_type') != 'location') or (
max_gps_accuracy is not None and
convert(data.get('acc'), float, 0.0) > max_gps_accuracy):
def owntracks_location_update(topic, payload, qos):
"""MQTT message received."""
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typelocation
data = validate_payload(payload, 'location')
if not data:
return
dev_id, kwargs = _parse_see_args(topic, data)
@ -65,24 +77,16 @@ def setup_scanner(hass, config, see):
see_beacons(dev_id, kwargs)
def owntracks_event_update(topic, payload, qos):
# pylint: disable=too-many-branches, too-many-statements
"""MQTT event (geofences) received."""
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typetransition
try:
data = json.loads(payload)
except ValueError:
# If invalid JSON
_LOGGER.error(
'Unable to parse payload as JSON: %s', payload)
return
if not isinstance(data, dict) or data.get('_type') != 'transition':
data = validate_payload(payload, 'transition')
if not data:
return
if data.get('desc') is None:
_LOGGER.error(
"Location missing from `enter/exit` message - "
"Location missing from `Entering/Leaving` message - "
"please turn `Share` on in OwnTracks app")
return
# OwnTracks uses - at the start of a beacon zone
@ -93,16 +97,16 @@ def setup_scanner(hass, config, see):
dev_id, kwargs = _parse_see_args(topic, data)
if data['event'] == 'enter':
def enter_event():
"""Execute enter event."""
zone = hass.states.get("zone.{}".format(location))
with LOCK:
if zone is None:
if data['t'] == 'b':
# Not a HA zone, and a beacon so assume mobile
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location not in beacons:
beacons.append(location)
_LOGGER.info("Added beacon %s", location)
if zone is None and data['t'] == 'b':
# Not a HA zone, and a beacon so assume mobile
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location not in beacons:
beacons.append(location)
_LOGGER.info("Added beacon %s", location)
else:
# Normal region
regions = REGIONS_ENTERED[dev_id]
@ -114,7 +118,8 @@ def setup_scanner(hass, config, see):
see(**kwargs)
see_beacons(dev_id, kwargs)
elif data['event'] == 'leave':
def leave_event():
"""Execute leave event."""
with LOCK:
regions = REGIONS_ENTERED[dev_id]
if location in regions:
@ -146,6 +151,10 @@ def setup_scanner(hass, config, see):
beacons.remove(location)
_LOGGER.info("Remove beacon %s", location)
if data['event'] == 'enter':
enter_event()
elif data['event'] == 'leave':
leave_event()
else:
_LOGGER.error(
'Misformatted mqtt msgs, _type=transition, event=%s',

View file

@ -15,7 +15,7 @@ from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED)
DOMAIN = "discovery"
REQUIREMENTS = ['netdisco==0.6.1']
REQUIREMENTS = ['netdisco==0.6.4']
SCAN_INTERVAL = 300 # seconds

View file

@ -10,8 +10,10 @@ import re
import threading
import requests
import voluptuous as vol
from homeassistant.helpers import validate_config
import homeassistant.helpers.config_validation as cv
from homeassistant.util import sanitize_filename
DOMAIN = "downloader"
@ -21,6 +23,11 @@ SERVICE_DOWNLOAD_FILE = "download_file"
ATTR_URL = "url"
ATTR_SUBDIR = "subdir"
SERVICE_DOWNLOAD_FILE_SCHEMA = vol.Schema({
vol.Required(ATTR_URL): vol.Url,
vol.Optional(ATTR_SUBDIR): cv.string,
})
CONF_DOWNLOAD_DIR = 'download_dir'
@ -48,10 +55,6 @@ def setup(hass, config):
def download_file(service):
"""Start thread to download file specified in the URL."""
if ATTR_URL not in service.data:
logger.error("Service called but 'url' parameter not specified.")
return
def do_download():
"""Download the file."""
try:
@ -127,7 +130,7 @@ def setup(hass, config):
threading.Thread(target=do_download).start()
hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE,
download_file)
hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE, download_file,
schema=SERVICE_DOWNLOAD_FILE_SCHEMA)
return True

View file

@ -0,0 +1,80 @@
"""RSS/Atom feed reader for Home Assistant."""
from datetime import datetime
from logging import getLogger
import voluptuous as vol
from homeassistant.helpers.event import track_utc_time_change
REQUIREMENTS = ['feedparser==5.2.1']
_LOGGER = getLogger(__name__)
DOMAIN = "feedreader"
EVENT_FEEDREADER = "feedreader"
# pylint: disable=no-value-for-parameter
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
'urls': [vol.Url()],
}
}, extra=vol.ALLOW_EXTRA)
# pylint: disable=too-few-public-methods
class FeedManager(object):
"""Abstraction over feedparser module."""
def __init__(self, url, hass):
"""Initialize the FeedManager object, poll every hour."""
self._url = url
self._feed = None
self._hass = hass
# Initialize last entry timestamp as epoch time
self._last_entry_timestamp = datetime.utcfromtimestamp(0).timetuple()
_LOGGER.debug('Loading feed %s', self._url)
self._update()
track_utc_time_change(hass, lambda now: self._update(),
minute=0, second=0)
def _log_no_entries(self):
"""Send no entries log at debug level."""
_LOGGER.debug('No new entries in feed %s', self._url)
def _update(self):
"""Update the feed and publish new entries in the event bus."""
import feedparser
_LOGGER.info('Fetching new data from feed %s', self._url)
self._feed = feedparser.parse(self._url,
etag=None if not self._feed
else self._feed.get('etag'),
modified=None if not self._feed
else self._feed.get('modified'))
if not self._feed:
_LOGGER.error('Error fetching feed data from %s', self._url)
else:
if self._feed.bozo != 0:
_LOGGER.error('Error parsing feed %s', self._url)
# Using etag and modified, if there's no new data available,
# the entries list will be empty
elif len(self._feed.entries) > 0:
_LOGGER.debug('Entries available in feed %s', self._url)
self._publish_new_entries()
self._last_entry_timestamp = \
self._feed.entries[0].published_parsed
else:
self._log_no_entries()
def _publish_new_entries(self):
"""Publish new entries to the event bus."""
new_entries = False
for entry in self._feed.entries:
# Consider only entries newer then the latest parsed one
if entry.published_parsed > self._last_entry_timestamp:
new_entries = True
entry.update({'feed_url': self._url})
self._hass.bus.fire(EVENT_FEEDREADER, entry)
if not new_entries:
self._log_no_entries()
def setup(hass, config):
"""Setup the feedreader component."""
urls = config.get(DOMAIN)['urls']
feeds = [FeedManager(url, hass) for url in urls]
return len(feeds) > 0

View file

@ -1,2 +1,2 @@
"""DO NOT MODIFY. Auto-generated by update_mdi script."""
VERSION = "df49e6b7c930eb39b42ff1909712e95e"
VERSION = "af8a531f1c2e477c07c4b3394bd1ce13"

View file

@ -1,2 +1,2 @@
"""DO NOT MODIFY. Auto-generated by build_frontend script."""
VERSION = "c2932592a6946e955ddc46f31409b81f"
VERSION = "ffd8a1bde5ba13f300c3d6ad32036526"

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit ac311416a99f41abbe98142ccac5f84f77d88296
Subproject commit 11311809c1eba0ed3b7e26d07a0fdb81b7959e3a

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

View file

@ -7,10 +7,13 @@ at https://home-assistant.io/components/garage_door/
import logging
import os
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
ATTR_ENTITY_ID)
@ -29,6 +32,10 @@ DISCOVERY_PLATFORMS = {
wink.DISCOVER_GARAGE_DOORS: 'wink'
}
GARAGE_DOOR_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
_LOGGER = logging.getLogger(__name__)
@ -73,10 +80,11 @@ def setup(hass, config):
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_OPEN, handle_garage_door_service,
descriptions.get(SERVICE_OPEN))
descriptions.get(SERVICE_OPEN),
schema=GARAGE_DOOR_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_CLOSE, handle_garage_door_service,
descriptions.get(SERVICE_CLOSE))
descriptions.get(SERVICE_CLOSE),
schema=GARAGE_DOOR_SERVICE_SCHEMA)
return True

View file

@ -0,0 +1,139 @@
"""
Support for MQTT garage doors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/garage_door.mqtt/
"""
import logging
import voluptuous as vol
from homeassistant.const import (STATE_OPEN, STATE_CLOSED, SERVICE_OPEN,
SERVICE_CLOSE)
import homeassistant.components.mqtt as mqtt
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import (
CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE)
from homeassistant.components.mqtt import (
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
CONF_STATE_OPEN = 'state_open'
CONF_STATE_CLOSED = 'state_closed'
CONF_SERVICE_OPEN = 'service_open'
CONF_SERVICE_CLOSE = 'service_close'
DEFAULT_NAME = 'MQTT Garage Door'
DEFAULT_OPTIMISTIC = False
DEFAULT_RETAIN = False
DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
vol.Optional(CONF_STATE_OPEN, default=STATE_OPEN): cv.string,
vol.Optional(CONF_STATE_CLOSED, default=STATE_CLOSED): cv.string,
vol.Optional(CONF_SERVICE_OPEN, default=SERVICE_OPEN): cv.string,
vol.Optional(CONF_SERVICE_CLOSE, default=SERVICE_CLOSE): cv.string
})
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Add MQTT Garage Door."""
add_devices_callback([MqttGarageDoor(
hass,
config[CONF_NAME],
config.get(CONF_STATE_TOPIC),
config[CONF_COMMAND_TOPIC],
config[CONF_QOS],
config[CONF_RETAIN],
config[CONF_STATE_OPEN],
config[CONF_STATE_CLOSED],
config[CONF_SERVICE_OPEN],
config[CONF_SERVICE_CLOSE],
config[CONF_OPTIMISTIC],
config.get(CONF_VALUE_TEMPLATE))])
# pylint: disable=too-many-arguments, too-many-instance-attributes
class MqttGarageDoor(GarageDoorDevice):
"""Representation of a MQTT garage door."""
def __init__(self, hass, name, state_topic, command_topic, qos, retain,
state_open, state_closed, service_open, service_close,
optimistic, value_template):
"""Initialize the garage door."""
self._hass = hass
self._name = name
self._state_topic = state_topic
self._command_topic = command_topic
self._qos = qos
self._retain = retain
self._state_open = state_open
self._state_closed = state_closed
self._service_open = service_open
self._service_close = service_close
self._optimistic = optimistic or state_topic is None
self._state = False
def message_received(topic, payload, qos):
"""A new MQTT message has been received."""
if value_template is not None:
payload = template.render_with_possible_json_value(
hass, value_template, payload)
if payload == self._state_open:
self._state = True
self.update_ha_state()
elif payload == self._state_closed:
self._state = False
self.update_ha_state()
if self._state_topic is None:
# Force into optimistic mode.
self._optimistic = True
else:
mqtt.subscribe(hass, self._state_topic, message_received,
self._qos)
@property
def name(self):
"""Return the name of the garage door if any."""
return self._name
@property
def is_opened(self):
"""Return true if door is closed."""
return self._state
@property
def is_closed(self):
"""Return true if door is closed."""
return self._state is False
@property
def assumed_state(self):
"""Return true if we do optimistic updates."""
return self._optimistic
def close_door(self):
"""Close the door."""
mqtt.publish(self.hass, self._command_topic, self._service_close,
self._qos, self._retain)
if self._optimistic:
# Optimistically assume that door has changed state.
self._state = False
self.update_ha_state()
def open_door(self):
"""Open the door."""
mqtt.publish(self.hass, self._command_topic, self._service_open,
self._qos, self._retain)
if self._optimistic:
# Optimistically assume that door has changed state.
self._state = True
self.update_ha_state()

View file

@ -9,7 +9,7 @@ import logging
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
def setup_platform(hass, config, add_devices, discovery_info=None):

View file

@ -182,7 +182,7 @@ def _api_history_period(handler, path_match, data):
one_day = timedelta(seconds=86400)
if date_str:
start_date = dt_util.date_str_to_date(date_str)
start_date = dt_util.parse_date(date_str)
if start_date is None:
handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST)

View file

@ -5,6 +5,7 @@ For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/
"""
import gzip
import hmac
import json
import logging
import ssl
@ -200,12 +201,22 @@ class RequestHandler(SimpleHTTPRequestHandler):
"Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY)
return
self.authenticated = (self.server.api_password is None or
self.headers.get(HTTP_HEADER_HA_AUTH) ==
self.server.api_password or
data.get(DATA_API_PASSWORD) ==
self.server.api_password or
self.verify_session())
if self.verify_session():
# The user has a valid session already
self.authenticated = True
elif self.server.api_password is None:
# No password is set, so everyone is authenticated
self.authenticated = True
elif hmac.compare_digest(self.headers.get(HTTP_HEADER_HA_AUTH, ''),
self.server.api_password):
# A valid auth header has been set
self.authenticated = True
elif hmac.compare_digest(data.get(DATA_API_PASSWORD, ''),
self.server.api_password):
# A valid password has been specified
self.authenticated = True
else:
self.authenticated = False
# we really shouldn't need to forward the password from here
if url.path not in [URL_ROOT, URL_API_EVENT_FORWARD]:

View file

@ -7,8 +7,10 @@ https://home-assistant.io/components/ifttt/
import logging
import requests
import voluptuous as vol
from homeassistant.helpers import validate_config
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@ -23,6 +25,13 @@ ATTR_VALUE3 = 'value3'
REQUIREMENTS = ['pyfttt==0.3']
SERVICE_TRIGGER_SCHEMA = vol.Schema({
vol.Required(ATTR_EVENT): cv.string,
vol.Optional(ATTR_VALUE1): cv.string,
vol.Optional(ATTR_VALUE2): cv.string,
vol.Optional(ATTR_VALUE3): cv.string,
})
def trigger(hass, event, value1=None, value2=None, value3=None):
"""Trigger a Maker IFTTT recipe."""
@ -44,12 +53,10 @@ def setup(hass, config):
def trigger_service(call):
"""Handle IFTTT trigger service calls."""
event = call.data.get(ATTR_EVENT)
event = call.data[ATTR_EVENT]
value1 = call.data.get(ATTR_VALUE1)
value2 = call.data.get(ATTR_VALUE2)
value3 = call.data.get(ATTR_VALUE3)
if event is None:
return
try:
import pyfttt as pyfttt
@ -57,6 +64,7 @@ def setup(hass, config):
except requests.exceptions.RequestException:
_LOGGER.exception("Error communicating with IFTTT")
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service)
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service,
schema=SERVICE_TRIGGER_SCHEMA)
return True

View file

@ -7,7 +7,8 @@ https://home-assistant.io/components/influxdb/
import logging
import homeassistant.util as util
from homeassistant.const import EVENT_STATE_CHANGED, STATE_UNKNOWN
from homeassistant.const import (EVENT_STATE_CHANGED, STATE_UNAVAILABLE,
STATE_UNKNOWN)
from homeassistant.helpers import state as state_helper
from homeassistant.helpers import validate_config
@ -70,8 +71,9 @@ def setup(hass, config):
def influx_event_listener(event):
"""Listen for new messages on the bus and sends them to Influx."""
state = event.data.get('new_state')
if state is None or state.state in (STATE_UNKNOWN, '') \
or state.entity_id in blacklist:
if state is None or state.state in (
STATE_UNKNOWN, '', STATE_UNAVAILABLE) or \
state.entity_id in blacklist:
return
try:

View file

@ -6,8 +6,11 @@ at https://home-assistant.io/components/input_boolean/
"""
import logging
import voluptuous as vol
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
@ -22,6 +25,10 @@ CONF_NAME = "name"
CONF_INITIAL = "initial"
CONF_ICON = "icon"
TOGGLE_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def is_on(hass, entity_id):
"""Test if input_boolean is True."""
@ -75,8 +82,10 @@ def setup(hass, config):
else:
input_b.turn_off()
hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, toggle_service,
schema=TOGGLE_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TURN_ON, toggle_service,
schema=TOGGLE_SERVICE_SCHEMA)
component.add_entities(entities)

View file

@ -6,7 +6,10 @@ at https://home-assistant.io/components/input_select/
"""
import logging
import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
@ -25,6 +28,11 @@ ATTR_OPTIONS = 'options'
SERVICE_SELECT_OPTION = 'select_option'
SERVICE_SELECT_OPTION_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_OPTION): cv.string,
})
def select_option(hass, entity_id, option):
"""Set input_select to False."""
@ -79,10 +87,11 @@ def setup(hass, config):
target_inputs = component.extract_from_service(call)
for input_select in target_inputs:
input_select.select_option(call.data.get(ATTR_OPTION))
input_select.select_option(call.data[ATTR_OPTION])
hass.services.register(DOMAIN, SERVICE_SELECT_OPTION,
select_option_service)
select_option_service,
schema=SERVICE_SELECT_OPTION_SCHEMA)
component.add_entities(entities)

View file

@ -6,7 +6,10 @@ at https://home-assistant.io/components/input_slider/
"""
import logging
import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
@ -29,6 +32,11 @@ ATTR_STEP = 'step'
SERVICE_SELECT_VALUE = 'select_value'
SERVICE_SELECT_VALUE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_VALUE): vol.Coerce(int),
})
def select_value(hass, entity_id, value):
"""Set input_slider to value."""
@ -81,10 +89,11 @@ def setup(hass, config):
target_inputs = component.extract_from_service(call)
for input_slider in target_inputs:
input_slider.select_value(call.data.get(ATTR_VALUE))
input_slider.select_value(call.data[ATTR_VALUE])
hass.services.register(DOMAIN, SERVICE_SELECT_VALUE,
select_value_service)
select_value_service,
schema=SERVICE_SELECT_VALUE_SCHEMA)
component.add_entities(entities)

View file

@ -4,6 +4,8 @@ Provides functionality to emulate keyboard presses on host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/keyboard/
"""
import voluptuous as vol
from homeassistant.const import (
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PLAY_PAUSE,
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
@ -12,6 +14,8 @@ from homeassistant.const import (
DOMAIN = "keyboard"
REQUIREMENTS = ['pyuserinput==0.1.9']
TAP_KEY_SCHEMA = vol.Schema({})
def volume_up(hass):
"""Press the keyboard button for volume up."""
@ -52,26 +56,31 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_VOLUME_UP,
lambda service:
keyboard.tap_key(keyboard.volume_up_key))
keyboard.tap_key(keyboard.volume_up_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN,
lambda service:
keyboard.tap_key(keyboard.volume_down_key))
keyboard.tap_key(keyboard.volume_down_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE,
lambda service:
keyboard.tap_key(keyboard.volume_mute_key))
keyboard.tap_key(keyboard.volume_mute_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
lambda service:
keyboard.tap_key(keyboard.media_play_pause_key))
keyboard.tap_key(keyboard.media_play_pause_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
lambda service:
keyboard.tap_key(keyboard.media_next_track_key))
keyboard.tap_key(keyboard.media_next_track_key),
schema=TAP_KEY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK,
lambda service:
keyboard.tap_key(keyboard.media_prev_track_key))
keyboard.tap_key(keyboard.media_prev_track_key),
schema=TAP_KEY_SCHEMA)
return True

View file

@ -32,6 +32,9 @@ PHUE_CONFIG_FILE = "phue.conf"
_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
# Track previously setup bridges
_CONFIGURED_BRIDGES = {}
def _find_host_from_config(hass, filename=PHUE_CONFIG_FILE):
"""Attempt to detect host based on existing configuration."""
@ -68,7 +71,8 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
return False
# Only act if we are not already configuring this host
if host in _CONFIGURING:
if host in _CONFIGURING or \
socket.gethostbyname(host) in _CONFIGURED_BRIDGES:
return
setup_bridge(host, hass, add_devices_callback, filename, allow_unreachable)
@ -142,6 +146,7 @@ def setup_bridge(host, hass, add_devices_callback, filename,
if new_lights:
add_devices_callback(new_lights)
_CONFIGURED_BRIDGES[socket.gethostbyname(host)] = True
update_lights()

View file

@ -19,7 +19,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup a Hyperion server remote."""
host = config.get(CONF_HOST, None)
port = config.get("port", 19444)
device = Hyperion(host, port)
device = Hyperion(config.get('name', host), host, port)
if device.setup():
add_devices_callback([device])
return True
@ -30,11 +30,11 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
class Hyperion(Light):
"""Representation of a Hyperion remote."""
def __init__(self, host, port):
def __init__(self, name, host, port):
"""Initialize the light."""
self._host = host
self._port = port
self._name = host
self._name = name
self._is_available = True
self._rgb_color = [255, 255, 255]
@ -75,7 +75,8 @@ class Hyperion(Light):
"""Get the hostname of the remote."""
response = self.json_request({"command": "serverinfo"})
if response:
self._name = response["info"]["hostname"]
if self._name == self._host:
self._name = response["info"]["hostname"]
return True
return False

View file

@ -209,6 +209,8 @@ class LIFXLight(Light):
brightness = self._bri
if ATTR_COLOR_TEMP in kwargs:
# pylint: disable=fixme
# TODO: Use color_temperature_mired_to_kelvin from util.color
kelvin = int(((TEMP_MAX - TEMP_MIN) *
(kwargs[ATTR_COLOR_TEMP] - TEMP_MIN_HASS) /
(TEMP_MAX_HASS - TEMP_MIN_HASS)) + TEMP_MIN)

View file

@ -6,8 +6,8 @@ https://home-assistant.io/components/light.mysensors/
"""
import logging
from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light)
from homeassistant.components.light import (ATTR_BRIGHTNESS, ATTR_RGB_COLOR,
Light)
from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
from homeassistant.loader import get_component
from homeassistant.util.color import rgb_hex_to_rgb_list
@ -100,15 +100,20 @@ class MySensorsLight(Light):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
device_attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
address = getattr(self.gateway, 'server_address', None)
if address:
device = '{}:{}'.format(address[0], address[1])
else:
device = self.gateway.port
attr = {
self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,
}
for value_type, value in self._values.items():
device_attr[self.gateway.const.SetReq(value_type).name] = value
return device_attr
attr[self.gateway.const.SetReq(value_type).name] = value
return attr
@property
def available(self):

View file

@ -72,8 +72,7 @@ class VeraLight(VeraDevice, Light):
last_tripped = self.vera_device.last_trip
if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time)
attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
else:
attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped

View file

@ -6,10 +6,14 @@ https://home-assistant.io/components/light.wink/
"""
import logging
from homeassistant.components.light import ATTR_BRIGHTNESS, Light
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, \
Light, ATTR_RGB_COLOR
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.util import color as color_util
from homeassistant.util.color import \
color_temperature_mired_to_kelvin as mired_to_kelvin
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
@ -35,7 +39,11 @@ class WinkLight(Light):
"""Representation of a Wink light."""
def __init__(self, wink):
"""Initialize the light."""
"""
Initialize the light.
:type wink: pywink.devices.standard.bulb.WinkBulb
"""
self.wink = wink
@property
@ -63,15 +71,41 @@ class WinkLight(Light):
"""True if connection == True."""
return self.wink.available
@property
def xy_color(self):
"""Current bulb color in CIE 1931 (XY) color space."""
if not self.wink.supports_xy_color():
return None
return self.wink.color_xy()
@property
def color_temp(self):
"""Current bulb color in degrees Kelvin."""
if not self.wink.supports_temperature():
return None
return color_util.color_temperature_kelvin_to_mired(
self.wink.color_temperature_kelvin())
# pylint: disable=too-few-public-methods
def turn_on(self, **kwargs):
"""Turn the switch on."""
brightness = kwargs.get(ATTR_BRIGHTNESS)
rgb_color = kwargs.get(ATTR_RGB_COLOR)
color_temp_mired = kwargs.get(ATTR_COLOR_TEMP)
if brightness is not None:
self.wink.set_state(True, brightness=brightness / 255)
else:
self.wink.set_state(True)
state_kwargs = {
}
if rgb_color:
state_kwargs['color_xy'] = color_util.color_RGB_to_xy(*rgb_color)
if color_temp_mired:
state_kwargs['color_kelvin'] = mired_to_kelvin(color_temp_mired)
if brightness:
state_kwargs['brightness'] = brightness / 255.0
self.wink.set_state(True, **state_kwargs)
def turn_off(self):
"""Turn the switch off."""
@ -79,4 +113,4 @@ class WinkLight(Light):
def update(self):
"""Update state of the light."""
self.wink.update_state()
self.wink.update_state(require_desired_state_fulfilled=True)

View file

@ -9,25 +9,23 @@ https://home-assistant.io/components/light.zwave/
from threading import Timer
from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN, Light
from homeassistant.components.zwave import (
ATTR_NODE_ID, ATTR_VALUE_ID, COMMAND_CLASS_SWITCH_MULTILEVEL, GENRE_USER,
NETWORK, TYPE_BYTE, ZWaveDeviceEntity)
from homeassistant.components import zwave
from homeassistant.const import STATE_OFF, STATE_ON
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Find and add Z-Wave lights."""
if discovery_info is None or NETWORK is None:
if discovery_info is None or zwave.NETWORK is None:
return
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
if value.command_class != COMMAND_CLASS_SWITCH_MULTILEVEL:
if value.command_class != zwave.COMMAND_CLASS_SWITCH_MULTILEVEL:
return
if value.type != TYPE_BYTE:
if value.type != zwave.TYPE_BYTE:
return
if value.genre != GENRE_USER:
if value.genre != zwave.GENRE_USER:
return
value.set_change_verified(False)
@ -42,7 +40,7 @@ def brightness_state(value):
return 255, STATE_OFF
class ZwaveDimmer(ZWaveDeviceEntity, Light):
class ZwaveDimmer(zwave.ZWaveDeviceEntity, Light):
"""Representation of a Z-Wave dimmer."""
# pylint: disable=too-many-arguments
@ -51,7 +49,7 @@ class ZwaveDimmer(ZWaveDeviceEntity, Light):
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN)
self._brightness, self._state = brightness_state(value)

View file

@ -8,10 +8,13 @@ from datetime import timedelta
import logging
import os
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED,
STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK)
@ -33,6 +36,11 @@ DISCOVERY_PLATFORMS = {
verisure.DISCOVER_LOCKS: 'verisure'
}
LOCK_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_CODE): cv.string,
})
_LOGGER = logging.getLogger(__name__)
@ -75,10 +83,7 @@ def setup(hass, config):
"""Handle calls to the lock services."""
target_locks = component.extract_from_service(service)
if ATTR_CODE not in service.data:
code = None
else:
code = service.data[ATTR_CODE]
code = service.data.get(ATTR_CODE)
for item in target_locks:
if service.service == SERVICE_LOCK:
@ -92,10 +97,11 @@ def setup(hass, config):
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_UNLOCK, handle_lock_service,
descriptions.get(SERVICE_UNLOCK))
descriptions.get(SERVICE_UNLOCK),
schema=LOCK_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_LOCK, handle_lock_service,
descriptions.get(SERVICE_LOCK))
descriptions.get(SERVICE_LOCK),
schema=LOCK_SERVICE_SCHEMA)
return True

View file

@ -9,7 +9,7 @@ import logging
from homeassistant.components.lock import LockDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.4']
REQUIREMENTS = ['python-wink==0.7.4']
def setup_platform(hass, config, add_devices, discovery_info=None):

View file

@ -9,6 +9,8 @@ import re
from datetime import timedelta
from itertools import groupby
import voluptuous as vol
import homeassistant.util.dt as dt_util
from homeassistant.components import recorder, sun
from homeassistant.const import (
@ -18,6 +20,7 @@ from homeassistant.core import DOMAIN as HA_DOMAIN
from homeassistant.core import State
from homeassistant.helpers.entity import split_entity_id
from homeassistant.helpers import template
import homeassistant.helpers.config_validation as cv
DOMAIN = "logbook"
DEPENDENCIES = ['recorder', 'http']
@ -39,6 +42,13 @@ ATTR_MESSAGE = 'message'
ATTR_DOMAIN = 'domain'
ATTR_ENTITY_ID = 'entity_id'
LOG_MESSAGE_SCHEMA = vol.Schema({
vol.Required(ATTR_NAME): cv.string,
vol.Required(ATTR_MESSAGE): cv.string,
vol.Optional(ATTR_DOMAIN): cv.slug,
vol.Optional(ATTR_ENTITY_ID): cv.entity_id,
})
def log_entry(hass, name, message, domain=None, entity_id=None):
"""Add an entry to the logbook."""
@ -58,19 +68,17 @@ def setup(hass, config):
"""Listen for download events to download files."""
def log_message(service):
"""Handle sending notification message service calls."""
message = service.data.get(ATTR_MESSAGE)
name = service.data.get(ATTR_NAME)
domain = service.data.get(ATTR_DOMAIN, None)
entity_id = service.data.get(ATTR_ENTITY_ID, None)
if not message or not name:
return
message = service.data[ATTR_MESSAGE]
name = service.data[ATTR_NAME]
domain = service.data.get(ATTR_DOMAIN)
entity_id = service.data.get(ATTR_ENTITY_ID)
message = template.render(hass, message)
log_entry(hass, name, message, domain, entity_id)
hass.http.register_path('GET', URL_LOGBOOK, _handle_get_logbook)
hass.services.register(DOMAIN, 'log', log_message)
hass.services.register(DOMAIN, 'log', log_message,
schema=LOG_MESSAGE_SCHEMA)
return True
@ -79,7 +87,7 @@ def _handle_get_logbook(handler, path_match, data):
date_str = path_match.group('date')
if date_str:
start_date = dt_util.date_str_to_date(date_str)
start_date = dt_util.parse_date(date_str)
if start_date is None:
handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST)
@ -114,7 +122,7 @@ class Entry(object):
def as_dict(self):
"""Convert entry to a dict to be used within JSON."""
return {
'when': dt_util.datetime_to_str(self.when),
'when': self.when,
'name': self.name,
'message': self.message,
'domain': self.domain,

View file

@ -61,6 +61,7 @@ ATTR_APP_ID = 'app_id'
ATTR_APP_NAME = 'app_name'
ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands'
ATTR_INPUT_SOURCE = 'source'
ATTR_INPUT_SOURCE_LIST = 'source_list'
MEDIA_TYPE_MUSIC = 'music'
MEDIA_TYPE_TVSHOW = 'tvshow'
@ -94,6 +95,7 @@ SERVICE_TO_METHOD = {
SERVICE_MEDIA_PAUSE: 'media_pause',
SERVICE_MEDIA_NEXT_TRACK: 'media_next_track',
SERVICE_MEDIA_PREVIOUS_TRACK: 'media_previous_track',
SERVICE_SELECT_SOURCE: 'select_source'
}
ATTR_TO_PROPERTY = [
@ -116,6 +118,7 @@ ATTR_TO_PROPERTY = [
ATTR_APP_NAME,
ATTR_SUPPORTED_MEDIA_COMMANDS,
ATTR_INPUT_SOURCE,
ATTR_INPUT_SOURCE_LIST,
]
# Service call validation schemas
@ -473,6 +476,11 @@ class MediaPlayerDevice(Entity):
"""Name of the current input source."""
return None
@property
def source_list(self):
"""List of available input sources."""
return None
@property
def supported_media_commands(self):
"""Flag media commands that are supported."""

View file

@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
])
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg'
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/hqdefault.jpg'
YOUTUBE_PLAYER_SUPPORT = \
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
@ -208,7 +208,8 @@ class DemoMusicPlayer(AbstractDemoPlayer):
@property
def media_image_url(self):
"""Return the image url of current playing media."""
return 'https://graph.facebook.com/107771475912710/picture'
return 'https://graph.facebook.com/v2.5/107771475912710/' \
'picture?type=large'
@property
def media_title(self):
@ -287,7 +288,7 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
@property
def media_image_url(self):
"""Return the image url of current playing media."""
return 'https://graph.facebook.com/HouseofCards/picture'
return 'https://graph.facebook.com/v2.5/HouseofCards/picture?width=400'
@property
def media_title(self):

View file

@ -10,14 +10,16 @@ import socket
from homeassistant.components.media_player import (
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,
SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON,
SUPPORT_VOLUME_SET, MediaPlayerDevice)
SUPPORT_VOLUME_SET, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_PLAYLIST,
MediaPlayerDevice)
from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['python-mpd2==0.5.5']
SUPPORT_MPD = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_TURN_OFF | \
SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
SUPPORT_TURN_ON | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
SUPPORT_PLAY_MEDIA
# pylint: disable=unused-argument
@ -64,7 +66,7 @@ class MpdDevice(MediaPlayerDevice):
"""Representation of a MPD server."""
# MPD confuses pylint
# pylint: disable=no-member, abstract-method
# pylint: disable=no-member, too-many-public-methods, abstract-method
def __init__(self, server, port, location, password):
"""Initialize the MPD device."""
import mpd
@ -203,3 +205,14 @@ class MpdDevice(MediaPlayerDevice):
def media_previous_track(self):
"""Service to send the MPD the command for previous track."""
self.client.previous()
def play_media(self, media_type, media_id):
"""Send the media player the command for playing a playlist."""
_LOGGER.info(str.format("Playing playlist: {0}", media_id))
if media_type == MEDIA_TYPE_PLAYLIST:
self.client.clear()
self.client.load(media_id)
self.client.play()
else:
_LOGGER.error(str.format("Invalid media type. Expected: {0}",
MEDIA_TYPE_PLAYLIST))

View file

@ -18,6 +18,7 @@ from homeassistant.const import (
DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING,
STATE_UNKNOWN)
from homeassistant.loader import get_component
from homeassistant.helpers.event import (track_utc_time_change)
REQUIREMENTS = ['plexapi==1.1.0']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
@ -113,6 +114,7 @@ def setup_plexserver(host, token, hass, add_devices_callback):
plex_clients = {}
plex_sessions = {}
track_utc_time_change(hass, lambda now: update_devices(), second=30)
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_devices():

View file

@ -6,6 +6,7 @@ https://home-assistant.io/components/media_player.sonos/
"""
import datetime
import logging
import socket
from homeassistant.components.media_player import (
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,
@ -13,7 +14,7 @@ from homeassistant.components.media_player import (
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
from homeassistant.const import (
STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN)
STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN, STATE_OFF)
REQUIREMENTS = ['SoCo==0.11.1']
@ -36,11 +37,13 @@ SUPPORT_SONOS = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Sonos platform."""
import soco
import socket
if discovery_info:
add_devices([SonosDevice(hass, soco.SoCo(discovery_info))])
return True
player = soco.SoCo(discovery_info)
if player.is_visible:
add_devices([SonosDevice(hass, player)])
return True
return False
players = None
hosts = config.get('hosts', None)
@ -138,9 +141,14 @@ class SonosDevice(MediaPlayerDevice):
"""Retrieve latest state."""
self._name = self._player.get_speaker_info()['zone_name'].replace(
' (R)', '').replace(' (L)', '')
self._status = self._player.get_current_transport_info().get(
'current_transport_state')
self._trackinfo = self._player.get_current_track_info()
if self.available:
self._status = self._player.get_current_transport_info().get(
'current_transport_state')
self._trackinfo = self._player.get_current_track_info()
else:
self._status = STATE_OFF
self._trackinfo = {}
@property
def volume_level(self):
@ -253,3 +261,15 @@ class SonosDevice(MediaPlayerDevice):
def play_media(self, media_type, media_id):
"""Send the play_media command to the media player."""
self._player.play_uri(media_id)
@property
def available(self):
"""Return True if player is reachable, False otherwise."""
try:
sock = socket.create_connection(
address=(self._player.ip_address, 1443),
timeout=3)
sock.close()
return True
except socket.error:
return False

View file

@ -0,0 +1,262 @@
"""
Support for interface with an LG WebOS TV.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.webostv/
"""
import logging
from datetime import timedelta
from urllib.parse import urlparse
import homeassistant.util as util
from homeassistant.components.media_player import (
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK,
SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP,
SUPPORT_SELECT_SOURCE, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL,
MediaPlayerDevice)
from homeassistant.const import (
CONF_HOST, STATE_OFF, STATE_PLAYING, STATE_PAUSED, STATE_UNKNOWN)
from homeassistant.loader import get_component
_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/TheRealLink/pylgtv'
'/archive/v0.1.2.zip'
'#pylgtv==0.1.2']
SUPPORT_WEBOSTV = SUPPORT_PAUSE | SUPPORT_VOLUME_STEP | \
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | \
SUPPORT_NEXT_TRACK | SUPPORT_TURN_OFF | \
SUPPORT_SELECT_SOURCE | SUPPORT_PLAY_MEDIA
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the LG WebOS TV platform."""
if discovery_info is not None:
host = urlparse(discovery_info[1]).hostname
else:
host = config.get(CONF_HOST, None)
if host is None:
_LOGGER.error('No host found in configuration')
return False
# Only act if we are not already configuring this host
if host in _CONFIGURING:
return
setup_tv(host, hass, add_devices)
def setup_tv(host, hass, add_devices):
"""Setup a phue bridge based on host parameter."""
from pylgtv import WebOsClient
from pylgtv import PyLGTVPairException
client = WebOsClient(host)
if not client.is_registered():
if host in _CONFIGURING:
# Try to pair.
try:
client.register()
except PyLGTVPairException:
_LOGGER.warning(
'Connected to LG WebOS TV at %s but not paired.', host)
return
except ConnectionRefusedError:
_LOGGER.error('Unable to connect to host %s.', host)
return
else:
# Not registered, request configuration.
_LOGGER.warning('LG WebOS TV at %s needs to be paired.', host)
request_configuration(host, hass, add_devices)
return
# If we came here and configuring this host, mark as done.
if client.is_registered() and host in _CONFIGURING:
request_id = _CONFIGURING.pop(host)
configurator = get_component('configurator')
configurator.request_done(request_id)
add_devices([LgWebOSDevice(host)])
def request_configuration(host, hass, add_devices):
"""Request configuration steps from the user."""
configurator = get_component('configurator')
# We got an error if this method is called while we are configuring
if host in _CONFIGURING:
configurator.notify_errors(
_CONFIGURING[host], 'Failed to pair, please try again.')
return
# pylint: disable=unused-argument
def lgtv_configuration_callback(data):
"""The actions to do when our configuration callback is called."""
setup_tv(host, hass, add_devices)
_CONFIGURING[host] = configurator.request_config(
hass, 'LG WebOS TV', lgtv_configuration_callback,
description='Click start and accept the pairing request on your tv.',
description_image='/static/images/config_webos.png',
submit_caption='Start pairing request'
)
# pylint: disable=abstract-method
# pylint: disable=too-many-instance-attributes
class LgWebOSDevice(MediaPlayerDevice):
"""Representation of a LG WebOS TV."""
# pylint: disable=too-many-public-methods
def __init__(self, host):
"""Initialize the webos device."""
from pylgtv import WebOsClient
self._client = WebOsClient(host)
self._name = 'LG WebOS TV Remote'
# Assume that the TV is not muted
self._muted = False
# Assume that the TV is in Play mode
self._playing = True
self._volume = 0
self._current_source = None
self._current_source_id = None
self._source_list = None
self._source_label_list = None
self._state = STATE_UNKNOWN
self._app_list = None
self.update()
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update(self):
"""Retrieve the latest data."""
try:
self._state = STATE_PLAYING
self._muted = self._client.get_muted()
self._volume = self._client.get_volume()
self._current_source_id = self._client.get_input()
self._source_list = {}
self._source_label_list = []
self._app_list = {}
for app in self._client.get_apps():
self._app_list[app['id']] = app
for source in self._client.get_inputs():
self._source_list[source['label']] = source
self._app_list[source['appId']] = source
self._source_label_list.append(source['label'])
if source['appId'] == self._current_source_id:
self._current_source = source['label']
except ConnectionRefusedError:
self._state = STATE_OFF
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._state
@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
return self._muted
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
return self._volume / 100.0
@property
def source(self):
"""Return the current input source."""
return self._current_source
@property
def source_list(self):
"""List of available input sources."""
return self._source_label_list
@property
def media_content_type(self):
"""Content type of current playing media."""
return MEDIA_TYPE_CHANNEL
@property
def media_image_url(self):
"""Image url of current playing media."""
return self._app_list[self._current_source_id]['icon']
@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
return SUPPORT_WEBOSTV
def turn_off(self):
"""Turn off media player."""
self._client.power_off()
def volume_up(self):
"""Volume up the media player."""
self._client.volume_up()
def volume_down(self):
"""Volume down media player."""
self._client.volume_down()
def set_volume_level(self, volume):
"""Set volume level, range 0..1."""
tv_volume = volume * 100
self._client.set_volume(tv_volume)
def mute_volume(self, mute):
"""Send mute command."""
self._muted = mute
self._client.set_mute(mute)
def media_play_pause(self):
"""Simulate play pause media player."""
if self._playing:
self.media_pause()
else:
self.media_play()
def select_source(self, source):
"""Select input source."""
self._current_source_id = self._source_list[source]['appId']
self._current_source = self._source_list[source]['label']
self._client.set_input(self._source_list[source]['id'])
def media_play(self):
"""Send play command."""
self._playing = True
self._state = STATE_PLAYING
self._client.play()
def media_pause(self):
"""Send media pause command to media player."""
self._playing = False
self._state = STATE_PAUSED
self._client.pause()
def media_next_track(self):
"""Send next track command."""
self._client.fast_forward()
def media_previous_track(self):
"""Send the previous track command."""
self._client.rewind()

View file

@ -8,13 +8,15 @@ import logging
from homeassistant.components.media_player import (
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
SUPPORT_SELECT_SOURCE, MediaPlayerDevice)
from homeassistant.const import STATE_OFF, STATE_ON
REQUIREMENTS = ['rxv==0.1.11']
_LOGGER = logging.getLogger(__name__)
SUPPORT_YAMAHA = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE
def setup_platform(hass, config, add_devices, discovery_info=None):
@ -34,6 +36,8 @@ class YamahaDevice(MediaPlayerDevice):
self._muted = False
self._volume = 0
self._pwstate = STATE_OFF
self._current_source = None
self._source_list = None
self.update()
self._name = name
@ -45,6 +49,8 @@ class YamahaDevice(MediaPlayerDevice):
self._pwstate = STATE_OFF
self._muted = self._receiver.mute
self._volume = (self._receiver.volume/100) + 1
self._current_source = self._receiver.input
self._source_list = list(self._receiver.inputs().keys())
@property
def name(self):
@ -66,6 +72,16 @@ class YamahaDevice(MediaPlayerDevice):
"""Boolean if volume is currently muted."""
return self._muted
@property
def source(self):
"""Return the current input source."""
return self._current_source
@property
def source_list(self):
"""List of available input sources."""
return self._source_list
@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
@ -89,3 +105,7 @@ class YamahaDevice(MediaPlayerDevice):
"""Turn the media player on."""
self._receiver.on = True
self._volume = (self._receiver.volume/100) + 1
def select_source(self, source):
"""Select input source."""
self._receiver.input = source

View file

@ -1,5 +1,5 @@
"""
Support for MQTT message handling..
Support for MQTT message handling.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/mqtt/
@ -102,13 +102,13 @@ MQTT_BASE_PLATFORM_SCHEMA = vol.Schema({
vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
})
# Sensor type platforms subscribe to mqtt events
# Sensor type platforms subscribe to MQTT events
MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
})
# Switch type platforms publish to mqtt and may subscribe
# Switch type platforms publish to MQTT and may subscribe
MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic,
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,

View file

@ -1,4 +1,9 @@
"""MQTT server."""
"""
Support for a local MQTT broker.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/mqtt/#use-the-embedded-broker
"""
import asyncio
import logging
import tempfile

View file

@ -5,33 +5,36 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.mysensors/
"""
import logging
import socket
import homeassistant.bootstrap as bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED, TEMP_CELCIUS,
CONF_OPTIMISTIC)
from homeassistant.const import (ATTR_DISCOVERED, ATTR_SERVICE,
CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
EVENT_PLATFORM_DISCOVERED, TEMP_CELSIUS)
from homeassistant.helpers import validate_config
CONF_GATEWAYS = 'gateways'
CONF_PORT = 'port'
CONF_DEVICE = 'device'
CONF_DEBUG = 'debug'
CONF_PERSISTENCE = 'persistence'
CONF_PERSISTENCE_FILE = 'persistence_file'
CONF_VERSION = 'version'
CONF_BAUD_RATE = 'baud_rate'
CONF_TCP_PORT = 'tcp_port'
DEFAULT_VERSION = '1.4'
DEFAULT_BAUD_RATE = 115200
DEFAULT_TCP_PORT = 5003
DOMAIN = 'mysensors'
DEPENDENCIES = []
REQUIREMENTS = [
'https://github.com/theolind/pymysensors/archive/'
'f0c928532167fb24823efa793ec21ca646fd37a6.zip#pymysensors==0.5']
'cc5d0b325e13c2b623fa934f69eea7cd4555f110.zip#pymysensors==0.6']
_LOGGER = logging.getLogger(__name__)
ATTR_NODE_ID = 'node_id'
ATTR_CHILD_ID = 'child_id'
ATTR_PORT = 'port'
ATTR_DEVICE = 'device'
GATEWAYS = None
@ -49,30 +52,39 @@ DISCOVERY_COMPONENTS = [
]
def setup(hass, config):
def setup(hass, config): # pylint: disable=too-many-locals
"""Setup the MySensors component."""
if not validate_config(config,
{DOMAIN: [CONF_GATEWAYS]},
_LOGGER):
return False
if not all(CONF_PORT in gateway
if not all(CONF_DEVICE in gateway
for gateway in config[DOMAIN][CONF_GATEWAYS]):
_LOGGER.error('Missing required configuration items '
'in %s: %s', DOMAIN, CONF_PORT)
'in %s: %s', DOMAIN, CONF_DEVICE)
return False
import mysensors.mysensors as mysensors
version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION))
is_metric = (hass.config.temperature_unit == TEMP_CELCIUS)
is_metric = (hass.config.temperature_unit == TEMP_CELSIUS)
persistence = config[DOMAIN].get(CONF_PERSISTENCE, True)
def setup_gateway(port, persistence, persistence_file, version, baud_rate):
def setup_gateway(device, persistence_file, baud_rate, tcp_port):
"""Return gateway after setup of the gateway."""
gateway = mysensors.SerialGateway(port, event_callback=None,
persistence=persistence,
persistence_file=persistence_file,
protocol_version=version,
baud=baud_rate)
try:
socket.inet_aton(device)
# valid ip address
gateway = mysensors.TCPGateway(
device, event_callback=None, persistence=persistence,
persistence_file=persistence_file, protocol_version=version,
port=tcp_port)
except OSError:
# invalid ip address
gateway = mysensors.SerialGateway(
device, event_callback=None, persistence=persistence,
persistence_file=persistence_file, protocol_version=version,
baud=baud_rate)
gateway.metric = is_metric
gateway.debug = config[DOMAIN].get(CONF_DEBUG, False)
optimistic = config[DOMAIN].get(CONF_OPTIMISTIC, False)
@ -93,22 +105,22 @@ def setup(hass, config):
return gateway
# Setup all ports from config
# Setup all devices from config
global GATEWAYS
GATEWAYS = {}
conf_gateways = config[DOMAIN][CONF_GATEWAYS]
if isinstance(conf_gateways, dict):
conf_gateways = [conf_gateways]
persistence = config[DOMAIN].get(CONF_PERSISTENCE, True)
for index, gway in enumerate(conf_gateways):
port = gway[CONF_PORT]
device = gway[CONF_DEVICE]
persistence_file = gway.get(
CONF_PERSISTENCE_FILE,
hass.config.path('mysensors{}.pickle'.format(index + 1)))
baud_rate = gway.get(CONF_BAUD_RATE, DEFAULT_BAUD_RATE)
GATEWAYS[port] = setup_gateway(
port, persistence, persistence_file, version, baud_rate)
tcp_port = gway.get(CONF_TCP_PORT, DEFAULT_TCP_PORT)
GATEWAYS[device] = setup_gateway(
device, persistence_file, baud_rate, tcp_port)
for (component, discovery_service) in DISCOVERY_COMPONENTS:
# Ensure component is loaded
@ -139,7 +151,7 @@ def pf_callback_factory(map_sv_types, devices, add_devices, entity_class):
if key in devices:
devices[key].update_ha_state(True)
continue
name = '{} {}.{}'.format(
name = '{} {} {}'.format(
gateway.sensors[node_id].sketch_name, node_id, child.id)
if isinstance(entity_class, dict):
device_class = entity_class[child.type]

View file

@ -4,6 +4,9 @@ Support for Nest thermostats.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/thermostat.nest/
"""
import logging
import socket
import voluptuous as vol
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
@ -20,14 +23,27 @@ CONFIG_SCHEMA = vol.Schema({
})
}, extra=vol.ALLOW_EXTRA)
_LOGGER = logging.getLogger(__name__)
def devices():
"""Generator returning list of devices and their location."""
try:
for structure in NEST.structures:
for device in structure.devices:
yield (structure, device)
except socket.error:
_LOGGER.error("Connection error logging into the nest web service.")
# pylint: disable=unused-argument
def setup(hass, config):
"""Setup the Nest thermostat component."""
global NEST
username = config[DOMAIN].get(CONF_USERNAME)
password = config[DOMAIN].get(CONF_PASSWORD)
conf = config[DOMAIN]
username = conf[CONF_USERNAME]
password = conf[CONF_PASSWORD]
import nest

View file

@ -8,11 +8,13 @@ from functools import partial
import logging
import os
import voluptuous as vol
import homeassistant.bootstrap as bootstrap
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers import config_per_platform, template
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME
DOMAIN = "notify"
@ -32,6 +34,13 @@ ATTR_DATA = 'data'
SERVICE_NOTIFY = "notify"
NOTIFY_SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_MESSAGE): cv.template,
vol.Optional(ATTR_TITLE, default=ATTR_TITLE_DEFAULT): cv.string,
vol.Optional(ATTR_TARGET): cv.string,
vol.Optional(ATTR_DATA): dict, # nobody seems to be using this (yet)
})
_LOGGER = logging.getLogger(__name__)
@ -71,13 +80,7 @@ def setup(hass, config):
def notify_message(notify_service, call):
"""Handle sending notification message service calls."""
message = call.data.get(ATTR_MESSAGE)
if message is None:
_LOGGER.error(
'Received call to %s without attribute %s',
call.service, ATTR_MESSAGE)
return
message = call.data[ATTR_MESSAGE]
title = template.render(
hass, call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT))
@ -91,7 +94,8 @@ def setup(hass, config):
service_call_handler = partial(notify_message, notify_service)
service_notify = p_config.get(CONF_NAME, SERVICE_NOTIFY)
hass.services.register(DOMAIN, service_notify, service_call_handler,
descriptions.get(SERVICE_NOTIFY))
descriptions.get(SERVICE_NOTIFY),
schema=NOTIFY_SERVICE_SCHEMA)
success = True
return success

View file

@ -44,12 +44,12 @@ class FileNotificationService(BaseNotificationService):
if os.stat(self.filepath).st_size == 0:
title = '{} notifications (Log started: {})\n{}\n'.format(
kwargs.get(ATTR_TITLE),
dt_util.strip_microseconds(dt_util.utcnow()),
dt_util.utcnow().isoformat(),
'-' * 80)
file.write(title)
if self.add_timestamp == 1:
text = '{} {}\n'.format(dt_util.utcnow(), message)
text = '{} {}\n'.format(dt_util.utcnow().isoformat(), message)
file.write(text)
else:
text = '{}\n'.format(message)

View file

@ -0,0 +1,62 @@
"""
LG WebOS TV notification service.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.webostv/
"""
import logging
from homeassistant.components.notify import (BaseNotificationService, DOMAIN)
from homeassistant.const import (CONF_HOST, CONF_NAME)
from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)
def get_service(hass, config):
"""Return the notify service."""
if not validate_config({DOMAIN: config}, {DOMAIN: [CONF_HOST, CONF_NAME]},
_LOGGER):
return None
host = config.get(CONF_HOST, None)
if not host:
_LOGGER.error('No host provided.')
return None
from pylgtv import WebOsClient
from pylgtv import PyLGTVPairException
client = WebOsClient(host)
try:
client.register()
except PyLGTVPairException:
_LOGGER.error('Pairing failed.')
return None
except ConnectionRefusedError:
_LOGGER.error('Host unreachable.')
return None
return LgWebOSNotificationService(client)
# pylint: disable=too-few-public-methods
class LgWebOSNotificationService(BaseNotificationService):
"""Implement the notification service for LG WebOS TV."""
def __init__(self, client):
"""Initialize the service."""
self._client = client
def send_message(self, message="", **kwargs):
"""Send a message to the tv."""
from pylgtv import PyLGTVPairException
try:
self._client.send_message(message)
except PyLGTVPairException:
_LOGGER.error('Pairing failed.')
except ConnectionRefusedError:
_LOGGER.error('Host unreachable.')

View file

@ -10,7 +10,10 @@ from homeassistant.components.notify import (
ATTR_TITLE, DOMAIN, BaseNotificationService)
from homeassistant.helpers import validate_config
REQUIREMENTS = ['sleekxmpp==1.3.1', 'dnspython3==1.12.0']
REQUIREMENTS = ['sleekxmpp==1.3.1',
'dnspython3==1.12.0',
'pyasn1==0.1.9',
'pyasn1-modules==0.0.8']
_LOGGER = logging.getLogger(__name__)
@ -22,20 +25,23 @@ def get_service(hass, config):
_LOGGER):
return None
return XmppNotificationService(config['sender'],
config['password'],
config['recipient'])
return XmppNotificationService(
config.get('sender'),
config.get('password'),
config.get('recipient'),
config.get('tls', True))
# pylint: disable=too-few-public-methods
class XmppNotificationService(BaseNotificationService):
"""Implement the notification service for Jabber (XMPP)."""
def __init__(self, sender, password, recipient):
def __init__(self, sender, password, recipient, tls):
"""Initialize the service."""
self._sender = sender
self._password = password
self._recipient = recipient
self._tls = tls
def send_message(self, message="", **kwargs):
"""Send a message to a user."""
@ -43,10 +49,10 @@ class XmppNotificationService(BaseNotificationService):
data = "{}: {}".format(title, message) if title else message
send_message(self._sender + '/home-assistant', self._password,
self._recipient, data)
self._recipient, self._tls, data)
def send_message(sender, password, recipient, message):
def send_message(sender, password, recipient, use_tls, message):
"""Send a message over XMPP."""
import sleekxmpp
@ -59,11 +65,11 @@ def send_message(sender, password, recipient, message):
logging.basicConfig(level=logging.ERROR)
self.use_tls = True
self.use_tls = use_tls
self.use_ipv6 = False
self.add_event_handler('failed_auth', self.check_credentials)
self.add_event_handler('session_start', self.start)
self.connect()
self.connect(use_tls=self.use_tls, use_ssl=False)
self.process()
def start(self, event):

View file

@ -478,7 +478,7 @@ class Recorder(threading.Thread):
def _adapt_datetime(datetimestamp):
"""Turn a datetime into an integer for in the DB."""
return dt_util.as_utc(datetimestamp.replace(microsecond=0)).timestamp()
return dt_util.as_utc(datetimestamp).timestamp()
def _verify_instance():

View file

@ -38,16 +38,22 @@ _LOGGER = logging.getLogger(__name__)
RFXOBJECT = None
def _validate_packetid(value):
def validate_packetid(value):
"""Validate that value is a valid packet id for rfxtrx."""
if get_rfx_object(value):
return value
else:
raise vol.Invalid('invalid packet id for {}'.format(value))
# Share between rfxtrx platforms
VALID_DEVICE_ID = vol.All(cv.string, vol.Lower)
VALID_SENSOR_DEVICE_ID = vol.All(VALID_DEVICE_ID,
vol.truth(lambda val:
val.startswith('sensor_')))
DEVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_NAME): cv.string,
vol.Required(ATTR_PACKETID): _validate_packetid,
vol.Required(ATTR_PACKETID): validate_packetid,
vol.Optional(ATTR_FIREEVENT, default=False): cv.boolean,
})
@ -182,7 +188,7 @@ def apply_received_command(event):
# Check if entity exists or previously added automatically
if device_id in RFX_DEVICES:
_LOGGER.debug(
"EntityID: %s light_update. Command: %s",
"EntityID: %s device_update. Command: %s",
device_id,
event.values['Command']
)
@ -251,7 +257,7 @@ class RfxtrxDevice(Entity):
@property
def is_on(self):
"""Return true if light is on."""
"""Return true if device is on."""
return self._state
@property
@ -260,7 +266,7 @@ class RfxtrxDevice(Entity):
return True
def turn_off(self, **kwargs):
"""Turn the light off."""
"""Turn the device off."""
self._send_command("turn_off")
def _send_command(self, command, brightness=0):

View file

@ -7,10 +7,13 @@ https://home-assistant.io/components/rollershutter/
import os
import logging
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.components import group
from homeassistant.const import (
SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_STOP,
@ -33,6 +36,10 @@ _LOGGER = logging.getLogger(__name__)
ATTR_CURRENT_POSITION = 'current_position'
ROLLERSHUTTER_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def is_open(hass, entity_id=None):
"""Return if the roller shutter is open based on the statemachine."""
@ -85,14 +92,16 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_MOVE_UP,
handle_rollershutter_service,
descriptions.get(SERVICE_MOVE_UP))
descriptions.get(SERVICE_MOVE_UP),
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_MOVE_DOWN,
handle_rollershutter_service,
descriptions.get(SERVICE_MOVE_DOWN))
descriptions.get(SERVICE_MOVE_DOWN),
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_STOP,
handle_rollershutter_service,
descriptions.get(SERVICE_STOP))
descriptions.get(SERVICE_STOP),
schema=ROLLERSHUTTER_SERVICE_SCHEMA)
return True

View file

@ -7,9 +7,12 @@ https://home-assistant.io/components/scene/
import logging
from collections import namedtuple
import voluptuous as vol
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, CONF_PLATFORM)
from homeassistant.helpers import extract_domain_configs
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@ -19,6 +22,10 @@ STATE = 'scening'
CONF_ENTITIES = "entities"
SCENE_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
SceneConfig = namedtuple('SceneConfig', ['name', 'states'])
@ -61,7 +68,8 @@ def setup(hass, config):
for scene in target_scenes:
scene.activate()
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service,
schema=SCENE_SERVICE_SCHEMA)
return True

View file

@ -109,6 +109,11 @@ CONFIG_SCHEMA = vol.Schema({
vol.Required(DOMAIN): {cv.slug: _SCRIPT_ENTRY_SCHEMA}
}, extra=vol.ALLOW_EXTRA)
SCRIPT_SERVICE_SCHEMA = vol.Schema({})
SCRIPT_TURN_ONOFF_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
def is_on(hass, entity_id):
"""Return if the switch is on based on the statemachine."""
@ -149,7 +154,8 @@ def setup(hass, config):
alias = cfg.get(CONF_ALIAS, object_id)
script = Script(object_id, alias, cfg[CONF_SEQUENCE])
component.add_entities((script,))
hass.services.register(DOMAIN, object_id, service_handler)
hass.services.register(DOMAIN, object_id, service_handler,
schema=SCRIPT_SERVICE_SCHEMA)
def turn_on_service(service):
"""Call a service to turn script on."""
@ -168,10 +174,12 @@ def setup(hass, config):
for script in component.extract_from_service(service):
script.toggle()
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on_service)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service)
hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on_service,
schema=SCRIPT_TURN_ONOFF_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service,
schema=SCRIPT_TURN_ONOFF_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TOGGLE, toggle_service,
schema=SCRIPT_TURN_ONOFF_SCHEMA)
return True

View file

@ -98,12 +98,11 @@ class SCSGate:
from scsgate.tasks import GetStatusTask
with self._devices_to_register_lock:
if len(self._devices_to_register) == 0:
return
_, device = self._devices_to_register.popitem()
self._devices[device.scs_id] = device
self._device_being_registered = device.scs_id
self._reactor.append_task(GetStatusTask(target=device.scs_id))
while len(self._devices_to_register) != 0:
_, device = self._devices_to_register.popitem()
self._devices[device.scs_id] = device
self._device_being_registered = device.scs_id
self._reactor.append_task(GetStatusTask(target=device.scs_id))
def is_device_registered(self, device_id):
"""Check whether a device is already registered or not."""

View file

@ -7,13 +7,13 @@ https://home-assistant.io/components/sensor.apcupsd/
import logging
from homeassistant.components import apcupsd
from homeassistant.const import TEMP_CELCIUS
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
DEPENDENCIES = [apcupsd.DOMAIN]
DEFAULT_NAME = "UPS Status"
SPECIFIC_UNITS = {
"ITEMP": TEMP_CELCIUS
"ITEMP": TEMP_CELSIUS
}
_LOGGER = logging.getLogger(__name__)

View file

@ -4,7 +4,7 @@ Demo platform that has a couple of fake sensors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
from homeassistant.const import ATTR_BATTERY_LEVEL, TEMP_CELCIUS
from homeassistant.const import ATTR_BATTERY_LEVEL, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
@ -12,7 +12,7 @@ from homeassistant.helpers.entity import Entity
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo sensors."""
add_devices([
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS, 12),
DemoSensor('Outside Temperature', 15.6, TEMP_CELSIUS, 12),
DemoSensor('Outside Humidity', 54, '%', None),
])

View file

@ -7,7 +7,7 @@ https://home-assistant.io/components/sensor.forecast/
import logging
from datetime import timedelta
from homeassistant.const import CONF_API_KEY, TEMP_CELCIUS
from homeassistant.const import CONF_API_KEY, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
@ -64,7 +64,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if 'units' in config:
units = config['units']
elif hass.config.temperature_unit == TEMP_CELCIUS:
elif hass.config.temperature_unit == TEMP_CELSIUS:
units = 'si'
else:
units = 'us'

View file

@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ["https://github.com/robbiet480/pygtfs/archive/"
"6b40d5fb30fd410cfaf637c901b5ed5a08c33e4c.zip#"
"432414b720c580fb2667a0a48f539118a2d95969.zip#"
"pygtfs==0.1.2"]
ICON = "mdi:train"

View file

@ -8,12 +8,13 @@ import logging
from homeassistant.helpers.entity import Entity
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.util import convert
_LOGGER = logging.getLogger(__name__)
DOMAIN = "loopenergy"
REQUIREMENTS = ['pyloopenergy==0.0.7']
REQUIREMENTS = ['pyloopenergy==0.0.10']
def setup_platform(hass, config, add_devices, discovery_info=None):
@ -24,6 +25,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
elec_secret = config.get('electricity_secret')
gas_serial = config.get('gas_serial')
gas_secret = config.get('gas_secret')
gas_type = config.get('gas_type', 'metric')
gas_calorific = convert(config.get('gas_calorific'), float, 39.11)
if not (elec_serial and elec_secret):
_LOGGER.error(
@ -39,11 +42,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"serial and secret tokens")
return None
if gas_type not in ['imperial', 'metric']:
_LOGGER.error(
"Configuration Error, 'gas_type' "
"can only be 'imperial' or 'metric' ")
return None
# pylint: disable=too-many-function-args
controller = pyloopenergy.LoopEnergy(
elec_serial,
elec_secret,
gas_serial,
gas_secret
gas_secret,
gas_type,
gas_calorific
)
def stop_loopenergy(event):

View file

@ -9,7 +9,7 @@ import logging
import requests
from homeassistant.components.sensor import DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, TEMP_CELCIUS
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import Entity
@ -96,7 +96,7 @@ class MfiSensor(Entity):
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
if self._port.tag == 'temperature':
return TEMP_CELCIUS
return TEMP_CELSIUS
elif self._port.tag == 'active_pwr':
return 'Watts'
elif self._port.model == 'Input Digital':

View file

@ -8,7 +8,7 @@ import logging
import homeassistant.components.modbus as modbus
from homeassistant.const import (
STATE_OFF, STATE_ON, TEMP_CELCIUS, TEMP_FAHRENHEIT)
STATE_OFF, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
@ -97,7 +97,7 @@ class ModbusSensor(Entity):
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
if self._unit == "C":
return TEMP_CELCIUS
return TEMP_CELSIUS
elif self._unit == "F":
return TEMP_FAHRENHEIT
else:

View file

@ -6,8 +6,8 @@ https://home-assistant.io/components/sensor.mysensors/
"""
import logging
from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON, TEMP_CELCIUS, TEMP_FAHRENHEIT)
from homeassistant.const import (ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON,
TEMP_CELSIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
@ -132,7 +132,7 @@ class MySensorsSensor(Entity):
"""Return the unit of measurement of this entity."""
set_req = self.gateway.const.SetReq
unit_map = {
set_req.V_TEMP: (TEMP_CELCIUS
set_req.V_TEMP: (TEMP_CELSIUS
if self.gateway.metric else TEMP_FAHRENHEIT),
set_req.V_HUM: '%',
set_req.V_DIMMER: '%',
@ -157,8 +157,13 @@ class MySensorsSensor(Entity):
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
address = getattr(self.gateway, 'server_address', None)
if address:
device = '{}:{}'.format(address[0], address[1])
else:
device = self.gateway.port
attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,

View file

@ -4,12 +4,13 @@ Support for Nest Thermostat Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.nest/
"""
import logging
import socket
import voluptuous as vol
import homeassistant.components.nest as nest
from homeassistant.const import TEMP_CELCIUS
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
TEMP_CELSIUS, CONF_PLATFORM, CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS
)
DEPENDENCIES = ['nest']
SENSOR_TYPES = ['humidity',
@ -19,49 +20,42 @@ SENSOR_TYPES = ['humidity',
'last_connection',
'battery_level']
WEATHER_VARIABLES = ['weather_condition', 'weather_temperature',
'weather_humidity',
'wind_speed', 'wind_direction']
JSON_VARIABLE_NAMES = {'weather_humidity': 'humidity',
'weather_temperature': 'temperature',
'weather_condition': 'condition',
'wind_speed': 'kph',
'wind_direction': 'direction'}
WEATHER_VARS = {'weather_humidity': 'humidity',
'weather_temperature': 'temperature',
'weather_condition': 'condition',
'wind_speed': 'kph',
'wind_direction': 'direction'}
SENSOR_UNITS = {'humidity': '%', 'battery_level': 'V',
'kph': 'kph', 'temperature': '°C'}
SENSOR_TEMP_TYPES = ['temperature', 'target']
_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + \
list(WEATHER_VARS.keys())
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): nest.DOMAIN,
vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Required(CONF_MONITORED_CONDITIONS): [vol.In(_VALID_SENSOR_TYPES)],
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Nest Sensor."""
logger = logging.getLogger(__name__)
try:
for structure in nest.NEST.structures:
for device in structure.devices:
for variable in config['monitored_conditions']:
if variable in SENSOR_TYPES:
add_devices([NestBasicSensor(structure,
device,
variable)])
elif variable in SENSOR_TEMP_TYPES:
add_devices([NestTempSensor(structure,
device,
variable)])
elif variable in WEATHER_VARIABLES:
json_variable = JSON_VARIABLE_NAMES.get(variable, None)
add_devices([NestWeatherSensor(structure,
device,
json_variable)])
else:
logger.error('Nest sensor type: "%s" does not exist',
variable)
except socket.error:
logger.error(
"Connection error logging into the nest web service."
)
for structure, device in nest.devices():
sensors = [NestBasicSensor(structure, device, variable)
for variable in config[CONF_MONITORED_CONDITIONS]
if variable in SENSOR_TYPES]
sensors += [NestTempSensor(structure, device, variable)
for variable in config[CONF_MONITORED_CONDITIONS]
if variable in SENSOR_TEMP_TYPES]
sensors += [NestWeatherSensor(structure, device,
WEATHER_VARS[variable])
for variable in config[CONF_MONITORED_CONDITIONS]
if variable in WEATHER_VARS]
add_devices(sensors)
class NestSensor(Entity):
@ -109,7 +103,7 @@ class NestTempSensor(NestSensor):
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return TEMP_CELCIUS
return TEMP_CELSIUS
@property
def state(self):

View file

@ -9,7 +9,7 @@ from datetime import timedelta
from homeassistant.components.sensor import DOMAIN
from homeassistant.const import (
CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, TEMP_CELCIUS)
CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS)
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
@ -22,7 +22,7 @@ REQUIREMENTS = [
_LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {
'temperature': ['Temperature', TEMP_CELCIUS, 'mdi:thermometer'],
'temperature': ['Temperature', TEMP_CELSIUS, 'mdi:thermometer'],
'co2': ['CO2', 'ppm', 'mdi:cloud'],
'pressure': ['Pressure', 'mbar', 'mdi:gauge'],
'noise': ['Noise', 'dB', 'mdi:volume-high'],

View file

@ -9,7 +9,7 @@ import os
import time
from glob import glob
from homeassistant.const import STATE_UNKNOWN, TEMP_CELCIUS
from homeassistant.const import STATE_UNKNOWN, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
BASE_DIR = '/sys/bus/w1/devices/'
@ -84,7 +84,7 @@ class OneWire(Entity):
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return TEMP_CELCIUS
return TEMP_CELSIUS
def update(self):
"""Get the latest data from the device."""

View file

@ -7,7 +7,7 @@ https://home-assistant.io/components/sensor.openweathermap/
import logging
from datetime import timedelta
from homeassistant.const import CONF_API_KEY, TEMP_CELCIUS, TEMP_FAHRENHEIT
from homeassistant.const import CONF_API_KEY, TEMP_CELSIUS, TEMP_FAHRENHEIT
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
@ -106,7 +106,7 @@ class OpenWeatherMapSensor(Entity):
if self.type == 'weather':
self._state = data.get_detailed_status()
elif self.type == 'temperature':
if self.temp_unit == TEMP_CELCIUS:
if self.temp_unit == TEMP_CELSIUS:
self._state = round(data.get_temperature('celsius')['temp'],
1)
elif self.temp_unit == TEMP_FAHRENHEIT:

View file

@ -5,23 +5,18 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.rest/
"""
import logging
from datetime import timedelta
import requests
from homeassistant.const import CONF_VALUE_TEMPLATE, STATE_UNKNOWN
from homeassistant.helpers.entity import Entity
from homeassistant.helpers import template
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'REST Sensor'
DEFAULT_METHOD = 'GET'
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
# pylint: disable=unused-variable
def setup_platform(hass, config, add_devices, discovery_info=None):
@ -96,7 +91,6 @@ class RestData(object):
self._verify_ssl = verify_ssl
self.data = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data from REST service with GET method."""
try:

View file

@ -6,18 +6,21 @@ https://home-assistant.io/components/sensor.rfxtrx/
"""
import logging
from collections import OrderedDict
import voluptuous as vol
import homeassistant.components.rfxtrx as rfxtrx
from homeassistant.const import TEMP_CELCIUS
from homeassistant.const import TEMP_CELSIUS
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify
from homeassistant.components.rfxtrx import (
ATTR_PACKETID, ATTR_NAME, ATTR_DATA_TYPE)
ATTR_AUTOMATIC_ADD, ATTR_PACKETID, ATTR_NAME,
CONF_DEVICES, ATTR_DATA_TYPE)
DEPENDENCIES = ['rfxtrx']
DATA_TYPES = OrderedDict([
('Temperature', TEMP_CELCIUS),
('Temperature', TEMP_CELSIUS),
('Humidity', '%'),
('Barometer', ''),
('Wind direction', ''),
@ -26,19 +29,48 @@ DATA_TYPES = OrderedDict([
('Total usage', 'W')])
_LOGGER = logging.getLogger(__name__)
DEVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_NAME, default=None): cv.string,
vol.Required(ATTR_PACKETID): rfxtrx.validate_packetid,
vol.Optional(ATTR_DATA_TYPE, default=None):
vol.In(list(DATA_TYPES.keys())),
})
def _valid_device(value):
"""Validate a dictionary of devices definitions."""
config = OrderedDict()
for key, device in value.items():
try:
key = rfxtrx.VALID_SENSOR_DEVICE_ID(key)
config[key] = DEVICE_SCHEMA(device)
if not config[key][ATTR_NAME]:
config[key][ATTR_NAME] = key
except vol.MultipleInvalid as ex:
raise vol.Invalid('Rfxtrx sensor {} is invalid: {}'
.format(key, ex))
return config
PLATFORM_SCHEMA = vol.Schema({
vol.Required("platform"): rfxtrx.DOMAIN,
vol.Required(CONF_DEVICES): vol.All(dict, _valid_device),
vol.Optional(ATTR_AUTOMATIC_ADD, default=False): cv.boolean,
}, extra=vol.ALLOW_EXTRA)
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Setup the RFXtrx platform."""
from RFXtrx import SensorEvent
sensors = []
for device_id, entity_info in config.get('devices', {}).items():
for device_id, entity_info in config['devices'].items():
if device_id in rfxtrx.RFX_DEVICES:
continue
_LOGGER.info("Add %s rfxtrx.sensor", entity_info[ATTR_NAME])
event = rfxtrx.get_rfx_object(entity_info[ATTR_PACKETID])
new_sensor = RfxtrxSensor(event, entity_info[ATTR_NAME],
entity_info.get(ATTR_DATA_TYPE, None))
entity_info[ATTR_DATA_TYPE])
rfxtrx.RFX_DEVICES[slugify(device_id)] = new_sensor
sensors.append(new_sensor)
@ -62,7 +94,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
return
# Add entity if not exist and the automatic_add is True
if config.get('automatic_add', True):
if config[ATTR_AUTOMATIC_ADD]:
pkt_id = "".join("{0:02x}".format(x) for x in event.data)
entity_name = "%s : %s" % (device_id, pkt_id)
_LOGGER.info(

View file

@ -23,6 +23,8 @@ ATTR_TARGET = 'Destination'
ATTR_REMAINING_TIME = 'Remaining time'
ICON = 'mdi:bus'
TIME_STR_FORMAT = "%H:%M"
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
@ -126,10 +128,10 @@ class PublicTransportData(object):
try:
self.times = [
dt_util.datetime_to_time_str(
dt_util.as_local(dt_util.utc_from_timestamp(
item['from']['departureTimestamp']))
)
dt_util.as_local(
dt_util.utc_from_timestamp(
item['from']['departureTimestamp'])).strftime(
TIME_STR_FORMAT)
for item in connections
]
self.times.append(

View file

@ -131,9 +131,9 @@ class SystemMonitorSensor(Entity):
elif self.type == 'ipv6_address':
self._state = psutil.net_if_addrs()[self.argument][1][1]
elif self.type == 'last_boot':
self._state = dt_util.datetime_to_date_str(
dt_util.as_local(
dt_util.utc_from_timestamp(psutil.boot_time())))
self._state = dt_util.as_local(
dt_util.utc_from_timestamp(psutil.boot_time())
).date().isoformat()
elif self.type == 'since_last_boot':
self._state = dt_util.utcnow() - dt_util.utc_from_timestamp(
psutil.boot_time())

View file

@ -10,7 +10,7 @@ from datetime import datetime
from homeassistant.components import tellduslive
from homeassistant.const import (
ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, TEMP_CELCIUS)
ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME, TEMP_CELSIUS)
from homeassistant.helpers.entity import Entity
ATTR_LAST_UPDATED = "time_last_updated"
@ -27,7 +27,7 @@ SENSOR_TYPE_WINDGUST = "wgust"
SENSOR_TYPE_WATT = "watt"
SENSOR_TYPES = {
SENSOR_TYPE_TEMP: ['Temperature', TEMP_CELCIUS, "mdi:thermometer"],
SENSOR_TYPE_TEMP: ['Temperature', TEMP_CELSIUS, "mdi:thermometer"],
SENSOR_TYPE_HUMIDITY: ['Humidity', '%', "mdi:water"],
SENSOR_TYPE_RAINRATE: ['Rain rate', 'mm', "mdi:water"],
SENSOR_TYPE_RAINTOTAL: ['Rain total', 'mm', "mdi:water"],

View file

@ -8,7 +8,7 @@ import logging
from collections import namedtuple
import homeassistant.util as util
from homeassistant.const import TEMP_CELCIUS
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.entity import Entity
DatatypeDescription = namedtuple("DatatypeDescription", ['name', 'unit'])
@ -25,7 +25,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensor_value_descriptions = {
tellcore_constants.TELLSTICK_TEMPERATURE:
DatatypeDescription(
'temperature', config.get('temperature_scale', TEMP_CELCIUS)),
'temperature', config.get('temperature_scale', TEMP_CELSIUS)),
tellcore_constants.TELLSTICK_HUMIDITY:
DatatypeDescription('humidity', '%'),

View file

@ -6,7 +6,7 @@ https://home-assistant.io/components/sensor.temper/
"""
import logging
from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME
from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME, TEMP_FAHRENHEIT
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
@ -56,7 +56,9 @@ class TemperSensor(Entity):
def update(self):
"""Retrieve latest state."""
try:
self.current_value = self.temper_device.get_temperature()
format_str = ('fahrenheit' if self.temp_unit == TEMP_FAHRENHEIT
else 'celsius')
self.current_value = self.temper_device.get_temperature(format_str)
except IOError:
_LOGGER.error('Failed to get temperature due to insufficient '
'permissions. Try running with "sudo"')

View file

@ -0,0 +1,114 @@
"""Support for ThinkingCleaner."""
import logging
from datetime import timedelta
import homeassistant.util as util
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/TheRealLink/pythinkingcleaner'
'/archive/v0.0.2.zip'
'#pythinkingcleaner==0.0.2']
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100)
SENSOR_TYPES = {
'battery': ['Battery', '%', 'mdi:battery'],
'state': ['State', None, None],
'capacity': ['Capacity', None, None],
}
STATES = {
'st_base': 'On homebase: Not Charging',
'st_base_recon': 'On homebase: Reconditioning Charging',
'st_base_full': 'On homebase: Full Charging',
'st_base_trickle': 'On homebase: Trickle Charging',
'st_base_wait': 'On homebase: Waiting',
'st_plug': 'Plugged in: Not Charging',
'st_plug_recon': 'Plugged in: Reconditioning Charging',
'st_plug_full': 'Plugged in: Full Charging',
'st_plug_trickle': 'Plugged in: Trickle Charging',
'st_plug_wait': 'Plugged in: Waiting',
'st_stopped': 'Stopped',
'st_clean': 'Cleaning',
'st_cleanstop': 'Stopped with cleaning',
'st_clean_spot': 'Spot cleaning',
'st_clean_max': 'Max cleaning',
'st_delayed': 'Delayed cleaning will start soon',
'st_dock': 'Searching Homebase',
'st_pickup': 'Roomba picked up',
'st_remote': 'Remote control driving',
'st_wait': 'Waiting for command',
'st_off': 'Off',
'st_error': 'Error',
'st_locate': 'Find me!',
'st_unknown': 'Unknown state',
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the ThinkingCleaner platform."""
from pythinkingcleaner import Discovery
discovery = Discovery()
devices = discovery.discover()
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
def update_devices():
"""Update all devices."""
for device_object in devices:
device_object.update()
dev = []
for device in devices:
for type_name in SENSOR_TYPES.keys():
dev.append(ThinkingCleanerSensor(device, type_name,
update_devices))
add_devices(dev)
class ThinkingCleanerSensor(Entity):
"""ThinkingCleaner Sensor."""
def __init__(self, tc_object, sensor_type, update_devices):
"""Initialize the ThinkingCleaner."""
self.type = sensor_type
self._tc_object = tc_object
self._update_devices = update_devices
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return self._tc_object.name + ' ' + SENSOR_TYPES[self.type][0]
@property
def icon(self):
"""Icon to use in the frontend, if any."""
return SENSOR_TYPES[self.type][2]
@property
def state(self):
"""Return the state of the device."""
return self._state
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._unit_of_measurement
def update(self):
"""Update the sensor."""
self._update_devices()
if self.type == 'battery':
self._state = self._tc_object.battery
elif self.type == 'state':
self._state = STATES[self._tc_object.status]
elif self.type == 'capacity':
self._state = self._tc_object.capacity

View file

@ -19,6 +19,8 @@ OPTION_TYPES = {
'time_utc': 'Time (UTC)',
}
TIME_STR_FORMAT = "%H:%M"
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Time and Date sensor."""
@ -70,9 +72,9 @@ class TimeDateSensor(Entity):
def update(self):
"""Get the latest data and updates the states."""
time_date = dt_util.utcnow()
time = dt_util.datetime_to_time_str(dt_util.as_local(time_date))
time_utc = dt_util.datetime_to_time_str(time_date)
date = dt_util.datetime_to_date_str(dt_util.as_local(time_date))
time = dt_util.as_local(time_date).strftime(TIME_STR_FORMAT)
time_utc = time_date.strftime(TIME_STR_FORMAT)
date = dt_util.as_local(time_date).date().isoformat()
# Calculate the beat (Swatch Internet Time) time without date.
hours, minutes, seconds = time_date.strftime('%H:%M:%S').split(':')

View file

@ -9,7 +9,7 @@ import logging
import homeassistant.util.dt as dt_util
from homeassistant.const import (
ATTR_ARMED, ATTR_BATTERY_LEVEL, ATTR_LAST_TRIP_TIME, ATTR_TRIPPED,
TEMP_CELCIUS, TEMP_FAHRENHEIT)
TEMP_CELSIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity
from homeassistant.components.vera import (
VeraDevice, VERA_DEVICES, VERA_CONTROLLER)
@ -65,8 +65,7 @@ class VeraSensor(VeraDevice, Entity):
last_tripped = self.vera_device.last_trip
if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
utc_time)
attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
else:
attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped
@ -85,7 +84,7 @@ class VeraSensor(VeraDevice, Entity):
if vera_temp_units == 'F':
self._temperature_units = TEMP_FAHRENHEIT
else:
self._temperature_units = TEMP_CELCIUS
self._temperature_units = TEMP_CELSIUS
if self.hass:
temp = self.hass.config.temperature(

Some files were not shown because too many files have changed in this diff Show more