mirror of https://github.com/home-assistant/core
Merge remote-tracking branch 'upstream/dev' into http-zeroconf
This commit is contained in:
commit
f30b406334
|
@ -1,3 +1,5 @@
|
|||
Feature requests should go in the forum: https://community.home-assistant.io/c/feature-requests
|
||||
|
||||
**Home Assistant release (`hass --version`):**
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
config/*
|
||||
!config/home-assistant.conf.default
|
||||
homeassistant/components/frontend/www_static/polymer/bower_components/*
|
||||
|
||||
# There is not a better solution afaik..
|
||||
!config/custom_components
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
homeassistant:
|
||||
# Omitted values in this section will be auto detected using freegeoip.net
|
||||
# Omitted values in this section will be auto detected using freegeoip.io
|
||||
|
||||
# Location required to calculate the time the sun rises and sets.
|
||||
# Cooridinates are also used for location for weather related components.
|
||||
# Google Maps can be used to determine more precise GPS cooridinates.
|
||||
# Coordinates are also used for location for weather related components.
|
||||
# Google Maps can be used to determine more precise GPS coordinates.
|
||||
latitude: 32.87336
|
||||
longitude: 117.22743
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ 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__)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import (
|
||||
event_decorators, service, config_per_platform, extract_domain_configs)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
@ -293,7 +294,10 @@ def from_config_file(config_path, hass=None, verbose=False, daemon=False,
|
|||
|
||||
enable_logging(hass, verbose, daemon, log_rotate_days)
|
||||
|
||||
config_dict = config_util.load_yaml_config_file(config_path)
|
||||
try:
|
||||
config_dict = config_util.load_yaml_config_file(config_path)
|
||||
except HomeAssistantError:
|
||||
return None
|
||||
|
||||
return from_config_dict(config_dict, hass, enable_log=False,
|
||||
skip_pip=skip_pip)
|
||||
|
|
|
@ -12,6 +12,7 @@ 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
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
||||
|
|
|
@ -6,43 +6,55 @@ https://home-assistant.io/components/alarm_control_panel.mqtt/
|
|||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.alarm_control_panel as alarm
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.const import (
|
||||
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
|
||||
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN,
|
||||
CONF_NAME)
|
||||
from homeassistant.components.mqtt import (
|
||||
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = "MQTT Alarm"
|
||||
DEFAULT_QOS = 0
|
||||
DEFAULT_PAYLOAD_DISARM = "DISARM"
|
||||
DEFAULT_PAYLOAD_ARM_HOME = "ARM_HOME"
|
||||
DEFAULT_PAYLOAD_ARM_AWAY = "ARM_AWAY"
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_PAYLOAD_DISARM = 'payload_disarm'
|
||||
CONF_PAYLOAD_ARM_HOME = 'payload_arm_home'
|
||||
CONF_PAYLOAD_ARM_AWAY = 'payload_arm_away'
|
||||
CONF_CODE = 'code'
|
||||
|
||||
DEFAULT_NAME = "MQTT Alarm"
|
||||
DEFAULT_DISARM = "DISARM"
|
||||
DEFAULT_ARM_HOME = "ARM_HOME"
|
||||
DEFAULT_ARM_AWAY = "ARM_AWAY"
|
||||
|
||||
PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Required(CONF_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||
vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||
vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
|
||||
vol.Optional(CONF_CODE): cv.string,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the MQTT platform."""
|
||||
if config.get('state_topic') is None:
|
||||
_LOGGER.error("Missing required variable: state_topic")
|
||||
return False
|
||||
|
||||
if config.get('command_topic') is None:
|
||||
_LOGGER.error("Missing required variable: command_topic")
|
||||
return False
|
||||
|
||||
add_devices([MqttAlarm(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('state_topic'),
|
||||
config.get('command_topic'),
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
config.get('payload_disarm', DEFAULT_PAYLOAD_DISARM),
|
||||
config.get('payload_arm_home', DEFAULT_PAYLOAD_ARM_HOME),
|
||||
config.get('payload_arm_away', DEFAULT_PAYLOAD_ARM_AWAY),
|
||||
config.get('code'))])
|
||||
config[CONF_NAME],
|
||||
config[CONF_STATE_TOPIC],
|
||||
config[CONF_COMMAND_TOPIC],
|
||||
config[CONF_QOS],
|
||||
config[CONF_PAYLOAD_DISARM],
|
||||
config[CONF_PAYLOAD_ARM_HOME],
|
||||
config[CONF_PAYLOAD_ARM_AWAY],
|
||||
config.get(CONF_CODE))])
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
|
@ -62,7 +74,7 @@ class MqttAlarm(alarm.AlarmControlPanel):
|
|||
self._payload_disarm = payload_disarm
|
||||
self._payload_arm_home = payload_arm_home
|
||||
self._payload_arm_away = payload_arm_away
|
||||
self._code = str(code) if code else None
|
||||
self._code = code
|
||||
|
||||
def message_received(topic, payload, qos):
|
||||
"""A new MQTT message has been received."""
|
||||
|
|
|
@ -4,26 +4,29 @@ Offer MQTT listening automation rules.
|
|||
For more details about this automation rule, please refer to the documentation
|
||||
at https://home-assistant.io/components/automation/#mqtt-trigger
|
||||
"""
|
||||
import logging
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.const import CONF_PLATFORM
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_TOPIC = 'topic'
|
||||
CONF_PAYLOAD = 'payload'
|
||||
|
||||
TRIGGER_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PLATFORM): mqtt.DOMAIN,
|
||||
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
|
||||
vol.Optional(CONF_PAYLOAD): cv.string,
|
||||
})
|
||||
|
||||
|
||||
def trigger(hass, config, action):
|
||||
"""Listen for state changes based on configuration."""
|
||||
topic = config.get(CONF_TOPIC)
|
||||
topic = config[CONF_TOPIC]
|
||||
payload = config.get(CONF_PAYLOAD)
|
||||
|
||||
if topic is None:
|
||||
logging.getLogger(__name__).error(
|
||||
"Missing configuration key %s", CONF_TOPIC)
|
||||
return False
|
||||
|
||||
def mqtt_automation_listener(msg_topic, msg_payload, qos):
|
||||
"""Listen for MQTT messages."""
|
||||
if payload is None or payload == msg_payload:
|
||||
|
|
|
@ -6,43 +6,50 @@ https://home-assistant.io/components/binary_sensor.mqtt/
|
|||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.components.binary_sensor import (BinarySensorDevice,
|
||||
SENSOR_CLASSES)
|
||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||
from homeassistant.const import CONF_NAME, CONF_VALUE_TEMPLATE
|
||||
from homeassistant.components.mqtt import CONF_STATE_TOPIC, CONF_QOS
|
||||
from homeassistant.helpers import template
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_SENSOR_CLASS = 'sensor_class'
|
||||
CONF_PAYLOAD_ON = 'payload_on'
|
||||
CONF_PAYLOAD_OFF = 'payload_off'
|
||||
|
||||
DEFAULT_NAME = 'MQTT Binary sensor'
|
||||
DEFAULT_QOS = 0
|
||||
DEFAULT_PAYLOAD_ON = 'ON'
|
||||
DEFAULT_PAYLOAD_OFF = 'OFF'
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SENSOR_CLASS, default=None):
|
||||
vol.Any(vol.In(SENSOR_CLASSES), vol.SetTo(None)),
|
||||
vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
|
||||
})
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Add MQTT binary sensor."""
|
||||
if config.get('state_topic') is None:
|
||||
_LOGGER.error('Missing required variable: state_topic')
|
||||
return False
|
||||
|
||||
sensor_class = config.get('sensor_class')
|
||||
if sensor_class not in SENSOR_CLASSES:
|
||||
_LOGGER.warning('Unknown sensor class: %s', sensor_class)
|
||||
sensor_class = None
|
||||
|
||||
add_devices([MqttBinarySensor(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('state_topic', None),
|
||||
sensor_class,
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
config.get('payload_on', DEFAULT_PAYLOAD_ON),
|
||||
config.get('payload_off', DEFAULT_PAYLOAD_OFF),
|
||||
config.get(CONF_VALUE_TEMPLATE))])
|
||||
config[CONF_NAME],
|
||||
config[CONF_STATE_TOPIC],
|
||||
config[CONF_SENSOR_CLASS],
|
||||
config[CONF_QOS],
|
||||
config[CONF_PAYLOAD_ON],
|
||||
config[CONF_PAYLOAD_OFF],
|
||||
config.get(CONF_VALUE_TEMPLATE)
|
||||
)])
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
|
|
|
@ -136,7 +136,7 @@ def setup(hass, config):
|
|||
'Home Alone']},
|
||||
'who_cooks': {'icon': 'mdi:panda',
|
||||
'initial': 'Anne Therese',
|
||||
'name': 'Who cooks today',
|
||||
'name': 'Cook today',
|
||||
'options': ['Paulus', 'Anne Therese']}}})
|
||||
# Set up input boolean
|
||||
bootstrap.setup_component(
|
||||
|
@ -144,6 +144,11 @@ def setup(hass, config):
|
|||
{'input_boolean': {'notify': {'icon': 'mdi:car',
|
||||
'initial': False,
|
||||
'name': 'Notify Anne Therese is home'}}})
|
||||
# Set up weblink
|
||||
bootstrap.setup_component(
|
||||
hass, 'weblink',
|
||||
{'weblink': {'entities': [{'name': 'Router',
|
||||
'url': 'http://192.168.1.1'}]}})
|
||||
# Setup configurator
|
||||
configurator_ids = []
|
||||
|
||||
|
|
|
@ -204,6 +204,7 @@ class DeviceTracker(object):
|
|||
return
|
||||
|
||||
# If no device can be found, create it
|
||||
dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
|
||||
device = Device(
|
||||
self.hass, self.consider_home, self.home_range, self.track_new,
|
||||
dev_id, mac, (host_name or dev_id).replace('_', ' '))
|
||||
|
|
|
@ -6,28 +6,27 @@ https://home-assistant.io/components/device_tracker.mqtt/
|
|||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant import util
|
||||
from homeassistant.components.mqtt import CONF_QOS
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_QOS = 'qos'
|
||||
CONF_DEVICES = 'devices'
|
||||
|
||||
DEFAULT_QOS = 0
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_DEVICES): {cv.string: mqtt.valid_subscribe_topic},
|
||||
})
|
||||
|
||||
|
||||
def setup_scanner(hass, config, see):
|
||||
"""Setup the MQTT tracker."""
|
||||
devices = config.get(CONF_DEVICES)
|
||||
qos = util.convert(config.get(CONF_QOS), int, DEFAULT_QOS)
|
||||
|
||||
if not isinstance(devices, dict):
|
||||
_LOGGER.error('Expected %s to be a dict, found %s', CONF_DEVICES,
|
||||
devices)
|
||||
return False
|
||||
devices = config[CONF_DEVICES]
|
||||
qos = config[CONF_QOS]
|
||||
|
||||
dev_id_lookup = {}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -80,6 +80,11 @@ def setup_scanner(hass, config, see):
|
|||
if not isinstance(data, dict) or data.get('_type') != 'transition':
|
||||
return
|
||||
|
||||
if data.get('desc') is None:
|
||||
_LOGGER.error(
|
||||
"Location missing from `enter/exit` message - "
|
||||
"please turn `Share` on in OwnTracks app")
|
||||
return
|
||||
# OwnTracks uses - at the start of a beacon zone
|
||||
# to switch on 'hold mode' - ignore this
|
||||
location = data['desc'].lstrip("-")
|
||||
|
|
|
@ -15,7 +15,7 @@ from homeassistant.const import (
|
|||
EVENT_PLATFORM_DISCOVERED)
|
||||
|
||||
DOMAIN = "discovery"
|
||||
REQUIREMENTS = ['netdisco==0.6.1']
|
||||
REQUIREMENTS = ['netdisco==0.6.2']
|
||||
|
||||
SCAN_INTERVAL = 300 # seconds
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
"""DO NOT MODIFY. Auto-generated by build_frontend script."""
|
||||
VERSION = "45956327284e66953bfbed5d172928b8"
|
||||
VERSION = "7baf9a2bf8d94448eeaa6a1ceaf8464e"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Copyright 2011 Google Inc. All Rights Reserved.
|
|
@ -0,0 +1,17 @@
|
|||
<p>Roboto has a dual nature. It has a mechanical skeleton and the forms are
|
||||
largely geometric. At the same time, the font features friendly and open
|
||||
curves. While some grotesks distort their letterforms to force a rigid rhythm,
|
||||
Roboto doesn’t compromise, allowing letters to be settled into their natural
|
||||
width. This makes for a more natural reading rhythm more commonly found in
|
||||
humanist and serif types.</p>
|
||||
|
||||
<p>This is the normal family, which can be used alongside the
|
||||
<a href="http://www.google.com/fonts/specimen/Roboto+Condensed">Roboto Condensed</a> family and the
|
||||
<a href="http://www.google.com/fonts/specimen/Roboto+Slab">Roboto Slab</a> family.</p>
|
||||
|
||||
<p>
|
||||
<b>Updated January 14 2015:</b>
|
||||
Christian Robertson and the Material Design team unveiled the latest version of Roboto at Google I/O last year, and it is now available from Google Fonts.
|
||||
Existing websites using Roboto via Google Fonts will start using the latest version automatically.
|
||||
If you have installed the fonts on your computer, please download them again and re-install.
|
||||
</p>
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,129 @@
|
|||
{
|
||||
"name": "Roboto",
|
||||
"designer": "Christian Robertson",
|
||||
"license": "Apache2",
|
||||
"visibility": "External",
|
||||
"category": "Sans Serif",
|
||||
"size": 86523,
|
||||
"fonts": [
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "normal",
|
||||
"weight": 100,
|
||||
"filename": "Roboto-Thin.ttf",
|
||||
"postScriptName": "Roboto-Thin",
|
||||
"fullName": "Roboto Thin",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "italic",
|
||||
"weight": 100,
|
||||
"filename": "Roboto-ThinItalic.ttf",
|
||||
"postScriptName": "Roboto-ThinItalic",
|
||||
"fullName": "Roboto Thin Italic",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "normal",
|
||||
"weight": 300,
|
||||
"filename": "Roboto-Light.ttf",
|
||||
"postScriptName": "Roboto-Light",
|
||||
"fullName": "Roboto Light",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "italic",
|
||||
"weight": 300,
|
||||
"filename": "Roboto-LightItalic.ttf",
|
||||
"postScriptName": "Roboto-LightItalic",
|
||||
"fullName": "Roboto Light Italic",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "normal",
|
||||
"weight": 400,
|
||||
"filename": "Roboto-Regular.ttf",
|
||||
"postScriptName": "Roboto-Regular",
|
||||
"fullName": "Roboto",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "italic",
|
||||
"weight": 400,
|
||||
"filename": "Roboto-Italic.ttf",
|
||||
"postScriptName": "Roboto-Italic",
|
||||
"fullName": "Roboto Italic",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "normal",
|
||||
"weight": 500,
|
||||
"filename": "Roboto-Medium.ttf",
|
||||
"postScriptName": "Roboto-Medium",
|
||||
"fullName": "Roboto Medium",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "italic",
|
||||
"weight": 500,
|
||||
"filename": "Roboto-MediumItalic.ttf",
|
||||
"postScriptName": "Roboto-MediumItalic",
|
||||
"fullName": "Roboto Medium Italic",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "normal",
|
||||
"weight": 700,
|
||||
"filename": "Roboto-Bold.ttf",
|
||||
"postScriptName": "Roboto-Bold",
|
||||
"fullName": "Roboto Bold",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "italic",
|
||||
"weight": 700,
|
||||
"filename": "Roboto-BoldItalic.ttf",
|
||||
"postScriptName": "Roboto-BoldItalic",
|
||||
"fullName": "Roboto Bold Italic",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "normal",
|
||||
"weight": 900,
|
||||
"filename": "Roboto-Black.ttf",
|
||||
"postScriptName": "Roboto-Black",
|
||||
"fullName": "Roboto Black",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto",
|
||||
"style": "italic",
|
||||
"weight": 900,
|
||||
"filename": "Roboto-BlackItalic.ttf",
|
||||
"postScriptName": "Roboto-BlackItalic",
|
||||
"fullName": "Roboto Black Italic",
|
||||
"copyright": "Copyright 2011 Google Inc. All Rights Reserved."
|
||||
}
|
||||
],
|
||||
"subsets": [
|
||||
"cyrillic",
|
||||
"cyrillic-ext",
|
||||
"greek",
|
||||
"greek-ext",
|
||||
"latin",
|
||||
"latin-ext",
|
||||
"menu",
|
||||
"vietnamese"
|
||||
],
|
||||
"dateAdded": "2013-01-09"
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,17 @@
|
|||
<p>
|
||||
Roboto Mono is a monospaced addition to the <a href="https://www.google.com/fonts/specimen/Roboto">Roboto</a> type family.
|
||||
Like the other members of the Roboto family, the fonts are optimized for readability on screens across a wide variety of devices and reading environments.
|
||||
While the monospaced version is related to its variable width cousin, it doesn’t hesitate to change forms to better fit the constraints of a monospaced environment.
|
||||
For example, narrow glyphs like ‘I’, ‘l’ and ‘i’ have added serifs for more even texture while wider glyphs are adjusted for weight.
|
||||
Curved caps like ‘C’ and ‘O’ take on the straighter sides from Roboto Condensed.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Special consideration is given to glyphs important for reading and writing software source code.
|
||||
Letters with similar shapes are easy to tell apart.
|
||||
Digit ‘1’, lowercase ‘l’ and capital ‘I’ are easily differentiated as are zero and the letter ‘O’.
|
||||
Punctuation important for code has also been considered.
|
||||
For example, the curly braces ‘{ }’ have exaggerated points to clearly differentiate them from parenthesis ‘( )’ and braces ‘[ ]’.
|
||||
Periods and commas are also exaggerated to identify them more quickly.
|
||||
The scale and weight of symbols commonly used as operators have also been optimized.
|
||||
</p>
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,111 @@
|
|||
{
|
||||
"name": "Roboto Mono",
|
||||
"designer": "Christian Robertson",
|
||||
"license": "Apache2",
|
||||
"visibility": "External",
|
||||
"category": "Monospace",
|
||||
"size": 51290,
|
||||
"fonts": [
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-Thin",
|
||||
"fullName": "Roboto Mono Thin",
|
||||
"style": "normal",
|
||||
"weight": 100,
|
||||
"filename": "RobotoMono-Thin.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-ThinItalic",
|
||||
"fullName": "Roboto Mono Thin Italic",
|
||||
"style": "italic",
|
||||
"weight": 100,
|
||||
"filename": "RobotoMono-ThinItalic.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-Light",
|
||||
"fullName": "Roboto Mono Light",
|
||||
"style": "normal",
|
||||
"weight": 300,
|
||||
"filename": "RobotoMono-Light.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-LightItalic",
|
||||
"fullName": "Roboto Mono Light Italic",
|
||||
"style": "italic",
|
||||
"weight": 300,
|
||||
"filename": "RobotoMono-LightItalic.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-Regular",
|
||||
"fullName": "Roboto Mono",
|
||||
"style": "normal",
|
||||
"weight": 400,
|
||||
"filename": "RobotoMono-Regular.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-Italic",
|
||||
"fullName": "Roboto Mono Italic",
|
||||
"style": "italic",
|
||||
"weight": 400,
|
||||
"filename": "RobotoMono-Italic.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-Medium",
|
||||
"fullName": "Roboto Mono Medium",
|
||||
"style": "normal",
|
||||
"weight": 500,
|
||||
"filename": "RobotoMono-Medium.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-MediumItalic",
|
||||
"fullName": "Roboto Mono Medium Italic",
|
||||
"style": "italic",
|
||||
"weight": 500,
|
||||
"filename": "RobotoMono-MediumItalic.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-Bold",
|
||||
"fullName": "Roboto Mono Bold",
|
||||
"style": "normal",
|
||||
"weight": 700,
|
||||
"filename": "RobotoMono-Bold.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
},
|
||||
{
|
||||
"name": "Roboto Mono",
|
||||
"postScriptName": "RobotoMono-BoldItalic",
|
||||
"fullName": "Roboto Mono Bold Italic",
|
||||
"style": "italic",
|
||||
"weight": 700,
|
||||
"filename": "RobotoMono-BoldItalic.ttf",
|
||||
"copyright": "Copyright 2015 Google Inc. All Rights Reserved."
|
||||
}
|
||||
],
|
||||
"subsets": [
|
||||
"cyrillic",
|
||||
"cyrillic-ext",
|
||||
"greek",
|
||||
"greek-ext",
|
||||
"latin",
|
||||
"latin-ext",
|
||||
"menu",
|
||||
"vietnamese"
|
||||
],
|
||||
"dateAdded": "2015-05-13"
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
Subproject commit 567d8e6fba2199b22e4e5d1730f8f74bb81a402b
|
||||
Subproject commit de852d20bd635796c1a2d15befae79e7d0a2fae6
|
|
@ -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()
|
|
@ -39,7 +39,7 @@ def _conf_preprocess(value):
|
|||
return value
|
||||
|
||||
_SINGLE_GROUP_CONFIG = vol.Schema(vol.All(_conf_preprocess, {
|
||||
vol.Required(CONF_ENTITIES): cv.entity_ids,
|
||||
vol.Optional(CONF_ENTITIES): vol.Any(None, cv.entity_ids),
|
||||
CONF_VIEW: bool,
|
||||
CONF_NAME: str,
|
||||
CONF_ICON: cv.icon,
|
||||
|
@ -145,7 +145,7 @@ def setup(hass, config):
|
|||
"""Setup all groups found definded in the configuration."""
|
||||
for object_id, conf in config.get(DOMAIN, {}).items():
|
||||
name = conf.get(CONF_NAME, object_id)
|
||||
entity_ids = conf[CONF_ENTITIES]
|
||||
entity_ids = conf.get(CONF_ENTITIES) or []
|
||||
icon = conf.get(CONF_ICON)
|
||||
view = conf.get(CONF_VIEW)
|
||||
|
||||
|
|
|
@ -77,7 +77,9 @@ def setup(hass, config):
|
|||
name='HTTP-server').start())
|
||||
|
||||
hass.http = server
|
||||
hass.config.api = rem.API(util.get_local_ip(), api_password, server_port,
|
||||
hass.config.api = rem.API(server_host if server_host != '0.0.0.0'
|
||||
else util.get_local_ip(),
|
||||
api_password, server_port,
|
||||
ssl_certificate is not None)
|
||||
|
||||
return True
|
||||
|
|
|
@ -92,8 +92,8 @@ LIGHT_TURN_ON_SCHEMA = vol.Schema({
|
|||
ATTR_XY_COLOR: vol.All(vol.ExactSequence((cv.small_float, cv.small_float)),
|
||||
vol.Coerce(tuple)),
|
||||
ATTR_COLOR_TEMP: vol.All(int, vol.Range(min=154, max=500)),
|
||||
ATTR_FLASH: [FLASH_SHORT, FLASH_LONG],
|
||||
ATTR_EFFECT: [EFFECT_COLORLOOP, EFFECT_RANDOM, EFFECT_WHITE],
|
||||
ATTR_FLASH: vol.In([FLASH_SHORT, FLASH_LONG]),
|
||||
ATTR_EFFECT: vol.In([EFFECT_COLORLOOP, EFFECT_RANDOM, EFFECT_WHITE]),
|
||||
})
|
||||
|
||||
LIGHT_TURN_OFF_SCHEMA = vol.Schema({
|
||||
|
|
|
@ -7,46 +7,85 @@ https://home-assistant.io/components/light.mqtt/
|
|||
import logging
|
||||
from functools import partial
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light)
|
||||
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.template import render_with_possible_json_value
|
||||
from homeassistant.util import convert
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_STATE_VALUE_TEMPLATE = 'state_value_template'
|
||||
CONF_BRIGHTNESS_STATE_TOPIC = 'brightness_state_topic'
|
||||
CONF_BRIGHTNESS_COMMAND_TOPIC = 'brightness_command_topic'
|
||||
CONF_BRIGHTNESS_VALUE_TEMPLATE = 'brightness_value_template'
|
||||
CONF_RGB_STATE_TOPIC = 'rgb_state_topic'
|
||||
CONF_RGB_COMMAND_TOPIC = 'rgb_command_topic'
|
||||
CONF_RGB_VALUE_TEMPLATE = 'rgb_value_template'
|
||||
CONF_PAYLOAD_ON = 'payload_on'
|
||||
CONF_PAYLOAD_OFF = 'payload_off'
|
||||
CONF_BRIGHTNESS_SCALE = 'brightness_scale'
|
||||
|
||||
DEFAULT_NAME = 'MQTT Light'
|
||||
DEFAULT_QOS = 0
|
||||
DEFAULT_PAYLOAD_ON = 'ON'
|
||||
DEFAULT_PAYLOAD_OFF = 'OFF'
|
||||
DEFAULT_OPTIMISTIC = False
|
||||
DEFAULT_BRIGHTNESS_SCALE = 255
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_STATE_VALUE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_BRIGHTNESS_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||
vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||
vol.Optional(CONF_BRIGHTNESS_VALUE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_RGB_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||
vol.Optional(CONF_RGB_COMMAND_TOPIC): mqtt.valid_publish_topic,
|
||||
vol.Optional(CONF_RGB_VALUE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
|
||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||
vol.Optional(CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE):
|
||||
vol.All(vol.Coerce(int), vol.Range(min=1)),
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Add MQTT Light."""
|
||||
if config.get('command_topic') is None:
|
||||
_LOGGER.error("Missing required variable: command_topic")
|
||||
return False
|
||||
|
||||
config.setdefault(CONF_STATE_VALUE_TEMPLATE,
|
||||
config.get(CONF_VALUE_TEMPLATE))
|
||||
add_devices_callback([MqttLight(
|
||||
hass,
|
||||
convert(config.get('name'), str, DEFAULT_NAME),
|
||||
{key: convert(config.get(key), str) for key in
|
||||
(typ + topic
|
||||
for typ in ('', 'brightness_', 'rgb_')
|
||||
for topic in ('state_topic', 'command_topic'))},
|
||||
{key: convert(config.get(key + '_value_template'), str)
|
||||
for key in ('state', 'brightness', 'rgb')},
|
||||
convert(config.get('qos'), int, DEFAULT_QOS),
|
||||
config[CONF_NAME],
|
||||
{
|
||||
'on': convert(config.get('payload_on'), str, DEFAULT_PAYLOAD_ON),
|
||||
'off': convert(config.get('payload_off'), str, DEFAULT_PAYLOAD_OFF)
|
||||
key: config.get(key) for key in (
|
||||
CONF_STATE_TOPIC,
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_BRIGHTNESS_STATE_TOPIC,
|
||||
CONF_BRIGHTNESS_COMMAND_TOPIC,
|
||||
CONF_RGB_STATE_TOPIC,
|
||||
CONF_RGB_COMMAND_TOPIC,
|
||||
)
|
||||
},
|
||||
convert(config.get('optimistic'), bool, DEFAULT_OPTIMISTIC),
|
||||
convert(config.get('brightness_scale'), int, DEFAULT_BRIGHTNESS_SCALE)
|
||||
{
|
||||
'state': config.get(CONF_STATE_VALUE_TEMPLATE),
|
||||
'brightness': config.get(CONF_BRIGHTNESS_VALUE_TEMPLATE),
|
||||
'rgb': config.get(CONF_RGB_VALUE_TEMPLATE)
|
||||
},
|
||||
config[CONF_QOS],
|
||||
config[CONF_RETAIN],
|
||||
{
|
||||
'on': config[CONF_PAYLOAD_ON],
|
||||
'off': config[CONF_PAYLOAD_OFF],
|
||||
},
|
||||
config[CONF_OPTIMISTIC],
|
||||
config[CONF_BRIGHTNESS_SCALE],
|
||||
)])
|
||||
|
||||
|
||||
|
@ -54,13 +93,14 @@ class MqttLight(Light):
|
|||
"""MQTT light."""
|
||||
|
||||
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
||||
def __init__(self, hass, name, topic, templates, qos, payload, optimistic,
|
||||
brightness_scale):
|
||||
def __init__(self, hass, name, topic, templates, qos, retain, payload,
|
||||
optimistic, brightness_scale):
|
||||
"""Initialize MQTT light."""
|
||||
self._hass = hass
|
||||
self._name = name
|
||||
self._topic = topic
|
||||
self._qos = qos
|
||||
self._retain = retain
|
||||
self._payload = payload
|
||||
self._optimistic = optimistic or topic["state_topic"] is None
|
||||
self._optimistic_rgb = optimistic or topic["rgb_state_topic"] is None
|
||||
|
@ -156,7 +196,8 @@ class MqttLight(Light):
|
|||
self._topic["rgb_command_topic"] is not None:
|
||||
|
||||
mqtt.publish(self._hass, self._topic["rgb_command_topic"],
|
||||
"{},{},{}".format(*kwargs[ATTR_RGB_COLOR]), self._qos)
|
||||
"{},{},{}".format(*kwargs[ATTR_RGB_COLOR]),
|
||||
self._qos, self._retain)
|
||||
|
||||
if self._optimistic_rgb:
|
||||
self._rgb = kwargs[ATTR_RGB_COLOR]
|
||||
|
@ -167,14 +208,14 @@ class MqttLight(Light):
|
|||
percent_bright = float(kwargs[ATTR_BRIGHTNESS]) / 255
|
||||
device_brightness = int(percent_bright * self._brightness_scale)
|
||||
mqtt.publish(self._hass, self._topic["brightness_command_topic"],
|
||||
device_brightness, self._qos)
|
||||
device_brightness, self._qos, self._retain)
|
||||
|
||||
if self._optimistic_brightness:
|
||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
should_update = True
|
||||
|
||||
mqtt.publish(self._hass, self._topic["command_topic"],
|
||||
self._payload["on"], self._qos)
|
||||
self._payload["on"], self._qos, self._retain)
|
||||
|
||||
if self._optimistic:
|
||||
# Optimistically assume that switch has changed state.
|
||||
|
@ -187,7 +228,7 @@ class MqttLight(Light):
|
|||
def turn_off(self, **kwargs):
|
||||
"""Turn the device off."""
|
||||
mqtt.publish(self._hass, self._topic["command_topic"],
|
||||
self._payload["off"], self._qos)
|
||||
self._payload["off"], self._qos, self._retain)
|
||||
|
||||
if self._optimistic:
|
||||
# Optimistically assume that switch has changed state.
|
||||
|
|
|
@ -13,6 +13,8 @@ DEPENDENCIES = ['rfxtrx']
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = rfxtrx.DEFAULT_SCHEMA
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Setup the RFXtrx platform."""
|
||||
|
|
|
@ -6,40 +6,51 @@ https://home-assistant.io/components/lock.mqtt/
|
|||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.components.lock import LockDevice
|
||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||
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)
|
||||
from homeassistant.helpers import template
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_PAYLOAD_LOCK = 'payload_lock'
|
||||
CONF_PAYLOAD_UNLOCK = 'payload_unlock'
|
||||
|
||||
DEFAULT_NAME = "MQTT Lock"
|
||||
DEFAULT_PAYLOAD_LOCK = "LOCK"
|
||||
DEFAULT_PAYLOAD_UNLOCK = "UNLOCK"
|
||||
DEFAULT_QOS = 0
|
||||
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_PAYLOAD_LOCK, default=DEFAULT_PAYLOAD_LOCK):
|
||||
cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_UNLOCK, default=DEFAULT_PAYLOAD_UNLOCK):
|
||||
cv.string,
|
||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||
})
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Setup the MQTT lock."""
|
||||
if config.get('command_topic') is None:
|
||||
_LOGGER.error("Missing required variable: command_topic")
|
||||
return False
|
||||
|
||||
add_devices_callback([MqttLock(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('state_topic'),
|
||||
config.get('command_topic'),
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
config.get('retain', DEFAULT_RETAIN),
|
||||
config.get('payload_lock', DEFAULT_PAYLOAD_LOCK),
|
||||
config.get('payload_unlock', DEFAULT_PAYLOAD_UNLOCK),
|
||||
config.get('optimistic', DEFAULT_OPTIMISTIC),
|
||||
config[CONF_NAME],
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config[CONF_COMMAND_TOPIC],
|
||||
config[CONF_QOS],
|
||||
config[CONF_RETAIN],
|
||||
config[CONF_PAYLOAD_LOCK],
|
||||
config[CONF_PAYLOAD_UNLOCK],
|
||||
config[CONF_OPTIMISTIC],
|
||||
config.get(CONF_VALUE_TEMPLATE))])
|
||||
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
'Living Room', 'eyU3bRy2x44',
|
||||
'♥♥ The Best Fireplace Video (3 hours)'),
|
||||
DemoYoutubePlayer('Bedroom', 'kxopViU98Xo', 'Epic sax guy 10 hours'),
|
||||
DemoMusicPlayer(), DemoTVShowPlayer(), DemoReceiver(),
|
||||
DemoMusicPlayer(), DemoTVShowPlayer(),
|
||||
])
|
||||
|
||||
|
||||
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 | \
|
||||
|
@ -35,9 +35,7 @@ MUSIC_PLAYER_SUPPORT = \
|
|||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||
|
||||
NETFLIX_PLAYER_SUPPORT = \
|
||||
SUPPORT_PAUSE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||
|
||||
RECEIVER_SUPPORT = SUPPORT_SELECT_SOURCE
|
||||
SUPPORT_PAUSE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE
|
||||
|
||||
|
||||
class AbstractDemoPlayer(MediaPlayerDevice):
|
||||
|
@ -210,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):
|
||||
|
@ -269,6 +268,7 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
|
|||
super().__init__('Lounge room')
|
||||
self._cur_episode = 1
|
||||
self._episode_count = 13
|
||||
self._source = 'dvd'
|
||||
|
||||
@property
|
||||
def media_content_id(self):
|
||||
|
@ -288,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):
|
||||
|
@ -315,6 +315,11 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
|
|||
"""Return the current running application."""
|
||||
return "Netflix"
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""Return the current input source."""
|
||||
return self._source
|
||||
|
||||
@property
|
||||
def supported_media_commands(self):
|
||||
"""Flag of media commands that are supported."""
|
||||
|
@ -340,28 +345,7 @@ class DemoTVShowPlayer(AbstractDemoPlayer):
|
|||
self._cur_episode += 1
|
||||
self.update_ha_state()
|
||||
|
||||
|
||||
class DemoReceiver(AbstractDemoPlayer):
|
||||
"""A Demo receiver that only supports changing source input."""
|
||||
|
||||
# We only implement the methods that we support
|
||||
# pylint: disable=abstract-method
|
||||
def __init__(self):
|
||||
"""Initialize the demo device."""
|
||||
super().__init__('receiver')
|
||||
self._source = 'dvd'
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""Return the current input source."""
|
||||
return self._source
|
||||
|
||||
def select_source(self, source):
|
||||
"""Set the input source."""
|
||||
self._source = source
|
||||
self.update_ha_state()
|
||||
|
||||
@property
|
||||
def supported_media_commands(self):
|
||||
"""Flag of media commands that are supported."""
|
||||
return RECEIVER_SUPPORT
|
||||
|
|
|
@ -9,7 +9,8 @@ import urllib
|
|||
|
||||
from homeassistant.components.media_player import (
|
||||
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK,
|
||||
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, MediaPlayerDevice)
|
||||
SUPPORT_PLAY_MEDIA, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
|
||||
MediaPlayerDevice)
|
||||
from homeassistant.const import (
|
||||
STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING)
|
||||
|
||||
|
@ -17,7 +18,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||
REQUIREMENTS = ['jsonrpc-requests==0.1']
|
||||
|
||||
SUPPORT_KODI = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK
|
||||
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK | \
|
||||
SUPPORT_PLAY_MEDIA
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
|
@ -268,3 +270,7 @@ class KodiDevice(MediaPlayerDevice):
|
|||
self._server.Player.Seek(players[0]['playerid'], time)
|
||||
|
||||
self.update_ha_state()
|
||||
|
||||
def play_media(self, media_type, media_id):
|
||||
"""Send the play_media command to the media player."""
|
||||
self._server.Player.Open({media_type: media_id}, {})
|
||||
|
|
|
@ -14,11 +14,11 @@ import voluptuous as vol
|
|||
from homeassistant.bootstrap import prepare_setup_platform
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.util as util
|
||||
from homeassistant.helpers import template
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import (
|
||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
||||
CONF_PLATFORM, CONF_SCAN_INTERVAL, CONF_VALUE_TEMPLATE)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -41,6 +41,11 @@ CONF_PASSWORD = 'password'
|
|||
CONF_CERTIFICATE = 'certificate'
|
||||
CONF_PROTOCOL = 'protocol'
|
||||
|
||||
CONF_STATE_TOPIC = 'state_topic'
|
||||
CONF_COMMAND_TOPIC = 'command_topic'
|
||||
CONF_QOS = 'qos'
|
||||
CONF_RETAIN = 'retain'
|
||||
|
||||
PROTOCOL_31 = '3.1'
|
||||
PROTOCOL_311 = '3.1.1'
|
||||
|
||||
|
@ -53,25 +58,71 @@ DEFAULT_PROTOCOL = PROTOCOL_311
|
|||
ATTR_TOPIC = 'topic'
|
||||
ATTR_PAYLOAD = 'payload'
|
||||
ATTR_PAYLOAD_TEMPLATE = 'payload_template'
|
||||
ATTR_QOS = 'qos'
|
||||
ATTR_RETAIN = 'retain'
|
||||
ATTR_QOS = CONF_QOS
|
||||
ATTR_RETAIN = CONF_RETAIN
|
||||
|
||||
MAX_RECONNECT_WAIT = 300 # seconds
|
||||
|
||||
|
||||
# Service call validation schema
|
||||
def mqtt_topic(value):
|
||||
"""Validate that we can publish using this MQTT topic."""
|
||||
if isinstance(value, str) and all(c not in value for c in '#+\0'):
|
||||
def valid_subscribe_topic(value, invalid_chars='\0'):
|
||||
"""Validate that we can subscribe using this MQTT topic."""
|
||||
if isinstance(value, str) and all(c not in value for c in invalid_chars):
|
||||
return vol.Length(min=1, max=65535)(value)
|
||||
raise vol.Invalid('Invalid MQTT topic name')
|
||||
|
||||
|
||||
def valid_publish_topic(value):
|
||||
"""Validate that we can publish using this MQTT topic."""
|
||||
return valid_subscribe_topic(value, invalid_chars='#+\0')
|
||||
|
||||
_VALID_QOS_SCHEMA = vol.All(vol.Coerce(int), vol.In([0, 1, 2]))
|
||||
_HBMQTT_CONFIG_SCHEMA = vol.Schema(dict)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Optional(CONF_CLIENT_ID): cv.string,
|
||||
vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE):
|
||||
vol.All(vol.Coerce(int), vol.Range(min=15)),
|
||||
vol.Optional(CONF_BROKER): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT):
|
||||
vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)),
|
||||
vol.Optional(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_CERTIFICATE): cv.isfile,
|
||||
vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL):
|
||||
vol.All(cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])),
|
||||
vol.Optional(CONF_EMBEDDED): _HBMQTT_CONFIG_SCHEMA,
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
MQTT_BASE_PLATFORM_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PLATFORM): DOMAIN,
|
||||
vol.Optional(CONF_SCAN_INTERVAL):
|
||||
vol.All(vol.Coerce(int), vol.Range(min=1)),
|
||||
vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
|
||||
})
|
||||
|
||||
# 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
|
||||
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,
|
||||
vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic,
|
||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||
})
|
||||
|
||||
|
||||
# Service call validation schema
|
||||
MQTT_PUBLISH_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_TOPIC): mqtt_topic,
|
||||
vol.Required(ATTR_TOPIC): valid_publish_topic,
|
||||
vol.Exclusive(ATTR_PAYLOAD, 'payload'): object,
|
||||
vol.Exclusive(ATTR_PAYLOAD_TEMPLATE, 'payload'): cv.string,
|
||||
vol.Required(ATTR_QOS, default=DEFAULT_QOS):
|
||||
vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
|
||||
vol.Required(ATTR_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA,
|
||||
vol.Required(ATTR_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
}, required=True)
|
||||
|
||||
|
@ -136,8 +187,8 @@ def setup(hass, config):
|
|||
# pylint: disable=too-many-locals
|
||||
conf = config.get(DOMAIN, {})
|
||||
|
||||
client_id = util.convert(conf.get(CONF_CLIENT_ID), str)
|
||||
keepalive = util.convert(conf.get(CONF_KEEPALIVE), int, DEFAULT_KEEPALIVE)
|
||||
client_id = conf.get(CONF_CLIENT_ID)
|
||||
keepalive = conf.get(CONF_KEEPALIVE)
|
||||
|
||||
broker_config = _setup_server(hass, config)
|
||||
|
||||
|
@ -151,16 +202,11 @@ def setup(hass, config):
|
|||
|
||||
if CONF_BROKER in conf:
|
||||
broker = conf[CONF_BROKER]
|
||||
port = util.convert(conf.get(CONF_PORT), int, DEFAULT_PORT)
|
||||
username = util.convert(conf.get(CONF_USERNAME), str)
|
||||
password = util.convert(conf.get(CONF_PASSWORD), str)
|
||||
certificate = util.convert(conf.get(CONF_CERTIFICATE), str)
|
||||
protocol = util.convert(conf.get(CONF_PROTOCOL), str, DEFAULT_PROTOCOL)
|
||||
|
||||
if protocol not in (PROTOCOL_31, PROTOCOL_311):
|
||||
_LOGGER.error('Invalid protocol specified: %s. Allowed values: %s, %s',
|
||||
protocol, PROTOCOL_31, PROTOCOL_311)
|
||||
return False
|
||||
port = conf[CONF_PORT]
|
||||
username = conf.get(CONF_USERNAME)
|
||||
password = conf.get(CONF_PASSWORD)
|
||||
certificate = conf.get(CONF_CERTIFICATE)
|
||||
protocol = conf[CONF_PROTOCOL]
|
||||
|
||||
# For cloudmqtt.com, secured connection, auto fill in certificate
|
||||
if certificate is None and 19999 < port < 30000 and \
|
||||
|
|
|
@ -6,9 +6,11 @@ https://home-assistant.io/components/mqtt_eventstream/
|
|||
"""
|
||||
import json
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.loader as loader
|
||||
from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN
|
||||
from homeassistant.components.mqtt import SERVICE_PUBLISH as MQTT_SVC_PUBLISH
|
||||
from homeassistant.components.mqtt import (
|
||||
valid_publish_topic, valid_subscribe_topic)
|
||||
from homeassistant.const import (
|
||||
ATTR_SERVICE_DATA, EVENT_CALL_SERVICE, EVENT_SERVICE_EXECUTED,
|
||||
EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL)
|
||||
|
@ -18,12 +20,23 @@ from homeassistant.remote import JSONEncoder
|
|||
DOMAIN = "mqtt_eventstream"
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_PUBLISH_TOPIC = 'publish_topic'
|
||||
CONF_SUBSCRIBE_TOPIC = 'subscribe_topic'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Optional(CONF_PUBLISH_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_SUBSCRIBE_TOPIC): valid_subscribe_topic,
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup th MQTT eventstream component."""
|
||||
"""Setup the MQTT eventstream component."""
|
||||
mqtt = loader.get_component('mqtt')
|
||||
pub_topic = config[DOMAIN].get('publish_topic', None)
|
||||
sub_topic = config[DOMAIN].get('subscribe_topic', None)
|
||||
conf = config.get(DOMAIN, {})
|
||||
pub_topic = conf.get(CONF_PUBLISH_TOPIC)
|
||||
sub_topic = conf.get(CONF_SUBSCRIBE_TOPIC)
|
||||
|
||||
def _event_publisher(event):
|
||||
"""Handle events by publishing them on the MQTT queue."""
|
||||
|
@ -36,8 +49,8 @@ def setup(hass, config):
|
|||
# to the MQTT topic, or you will end up in an infinite loop.
|
||||
if event.event_type == EVENT_CALL_SERVICE:
|
||||
if (
|
||||
event.data.get('domain') == MQTT_DOMAIN and
|
||||
event.data.get('service') == MQTT_SVC_PUBLISH and
|
||||
event.data.get('domain') == mqtt.DOMAIN and
|
||||
event.data.get('service') == mqtt.SERVICE_PUBLISH and
|
||||
event.data[ATTR_SERVICE_DATA].get('topic') == pub_topic
|
||||
):
|
||||
return
|
||||
|
|
|
@ -9,7 +9,8 @@ import logging
|
|||
import homeassistant.bootstrap as bootstrap
|
||||
from homeassistant.const import (
|
||||
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
|
||||
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED, TEMP_CELCIUS)
|
||||
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED, TEMP_CELCIUS,
|
||||
CONF_OPTIMISTIC)
|
||||
from homeassistant.helpers import validate_config
|
||||
|
||||
CONF_GATEWAYS = 'gateways'
|
||||
|
@ -19,7 +20,6 @@ CONF_PERSISTENCE = 'persistence'
|
|||
CONF_PERSISTENCE_FILE = 'persistence_file'
|
||||
CONF_VERSION = 'version'
|
||||
CONF_BAUD_RATE = 'baud_rate'
|
||||
CONF_OPTIMISTIC = 'optimistic'
|
||||
DEFAULT_VERSION = '1.4'
|
||||
DEFAULT_BAUD_RATE = 115200
|
||||
|
||||
|
|
|
@ -168,6 +168,7 @@ class RecorderRun(object):
|
|||
class Recorder(threading.Thread):
|
||||
"""A threaded recorder class."""
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
def __init__(self, hass):
|
||||
"""Initialize the recorder."""
|
||||
threading.Thread.__init__(self)
|
||||
|
@ -179,6 +180,7 @@ class Recorder(threading.Thread):
|
|||
self.lock = threading.Lock()
|
||||
self.recording_start = dt_util.utcnow()
|
||||
self.utc_offset = dt_util.now().utcoffset().total_seconds()
|
||||
self.db_path = self.hass.config.path(DB_FILE)
|
||||
|
||||
def start_recording(event):
|
||||
"""Start recording."""
|
||||
|
@ -302,8 +304,7 @@ class Recorder(threading.Thread):
|
|||
|
||||
def _setup_connection(self):
|
||||
"""Ensure database is ready to fly."""
|
||||
db_path = self.hass.config.path(DB_FILE)
|
||||
self.conn = sqlite3.connect(db_path, check_same_thread=False)
|
||||
self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
|
||||
self.conn.row_factory = sqlite3.Row
|
||||
|
||||
# Make sure the database is closed whenever Python exits
|
||||
|
|
|
@ -5,15 +5,19 @@ For more details about this component, please refer to the documentation at
|
|||
https://home-assistant.io/components/rfxtrx/
|
||||
"""
|
||||
import logging
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util import slugify
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
|
||||
REQUIREMENTS = ['pyRFXtrx==0.6.5']
|
||||
|
||||
DOMAIN = "rfxtrx"
|
||||
|
||||
ATTR_AUTOMATIC_ADD = 'automatic_add'
|
||||
ATTR_DEVICE = 'device'
|
||||
ATTR_DEBUG = 'debug'
|
||||
ATTR_STATE = 'state'
|
||||
|
@ -21,8 +25,10 @@ ATTR_NAME = 'name'
|
|||
ATTR_PACKETID = 'packetid'
|
||||
ATTR_FIREEVENT = 'fire_event'
|
||||
ATTR_DATA_TYPE = 'data_type'
|
||||
ATTR_DUMMY = "dummy"
|
||||
SIGNAL_REPETITIONS = 1
|
||||
ATTR_DUMMY = 'dummy'
|
||||
CONF_SIGNAL_REPETITIONS = 'signal_repetitions'
|
||||
CONF_DEVICES = 'devices'
|
||||
DEFAULT_SIGNAL_REPETITIONS = 1
|
||||
|
||||
EVENT_BUTTON_PRESSED = 'button_pressed'
|
||||
|
||||
|
@ -32,6 +38,42 @@ _LOGGER = logging.getLogger(__name__)
|
|||
RFXOBJECT = None
|
||||
|
||||
|
||||
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.Optional(ATTR_FIREEVENT, default=False): cv.boolean,
|
||||
})
|
||||
|
||||
DEFAULT_SCHEMA = vol.Schema({
|
||||
vol.Required("platform"): DOMAIN,
|
||||
vol.Required(CONF_DEVICES): {cv.slug: DEVICE_SCHEMA},
|
||||
vol.Optional(ATTR_AUTOMATIC_ADD, default=False): cv.boolean,
|
||||
vol.Optional(CONF_SIGNAL_REPETITIONS, default=DEFAULT_SIGNAL_REPETITIONS):
|
||||
vol.Coerce(int),
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Required(ATTR_DEVICE): VALID_DEVICE_ID,
|
||||
vol.Optional(ATTR_DEBUG, default=False): cv.boolean,
|
||||
vol.Optional(ATTR_DUMMY, default=False): cv.boolean,
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup the RFXtrx component."""
|
||||
# Declare the Handle event
|
||||
|
@ -56,16 +98,9 @@ def setup(hass, config):
|
|||
# Init the rfxtrx module.
|
||||
global RFXOBJECT
|
||||
|
||||
if ATTR_DEVICE not in config[DOMAIN]:
|
||||
_LOGGER.error(
|
||||
"can not find device parameter in %s YAML configuration section",
|
||||
DOMAIN
|
||||
)
|
||||
return False
|
||||
|
||||
device = config[DOMAIN][ATTR_DEVICE]
|
||||
debug = config[DOMAIN].get(ATTR_DEBUG, False)
|
||||
dummy_connection = config[DOMAIN].get(ATTR_DUMMY, False)
|
||||
debug = config[DOMAIN][ATTR_DEBUG]
|
||||
dummy_connection = config[DOMAIN][ATTR_DUMMY]
|
||||
|
||||
if dummy_connection:
|
||||
RFXOBJECT =\
|
||||
|
@ -103,16 +138,16 @@ def get_rfx_object(packetid):
|
|||
|
||||
def get_devices_from_config(config, device):
|
||||
"""Read rfxtrx configuration."""
|
||||
signal_repetitions = config.get('signal_repetitions', SIGNAL_REPETITIONS)
|
||||
signal_repetitions = config[CONF_SIGNAL_REPETITIONS]
|
||||
|
||||
devices = []
|
||||
for device_id, entity_info in config.get('devices', {}).items():
|
||||
for device_id, entity_info in config[CONF_DEVICES].items():
|
||||
if device_id in RFX_DEVICES:
|
||||
continue
|
||||
_LOGGER.info("Add %s rfxtrx", entity_info[ATTR_NAME])
|
||||
|
||||
# Check if i must fire event
|
||||
fire_event = entity_info.get(ATTR_FIREEVENT, False)
|
||||
fire_event = entity_info[ATTR_FIREEVENT]
|
||||
datas = {ATTR_STATE: False, ATTR_FIREEVENT: fire_event}
|
||||
|
||||
rfxobject = get_rfx_object(entity_info[ATTR_PACKETID])
|
||||
|
@ -127,7 +162,7 @@ def get_new_device(event, config, device):
|
|||
"""Add entity if not exist and the automatic_add is True."""
|
||||
device_id = slugify(event.device.id_string.lower())
|
||||
if device_id not in RFX_DEVICES:
|
||||
automatic_add = config.get('automatic_add', False)
|
||||
automatic_add = config[ATTR_AUTOMATIC_ADD]
|
||||
if not automatic_add:
|
||||
return
|
||||
|
||||
|
@ -140,8 +175,7 @@ def get_new_device(event, config, device):
|
|||
pkt_id = "".join("{0:02x}".format(x) for x in event.data)
|
||||
entity_name = "%s : %s" % (device_id, pkt_id)
|
||||
datas = {ATTR_STATE: False, ATTR_FIREEVENT: False}
|
||||
signal_repetitions = config.get('signal_repetitions',
|
||||
SIGNAL_REPETITIONS)
|
||||
signal_repetitions = config[CONF_SIGNAL_REPETITIONS]
|
||||
new_device = device(entity_name, event, datas,
|
||||
signal_repetitions)
|
||||
RFX_DEVICES[device_id] = new_device
|
||||
|
@ -178,8 +212,6 @@ def apply_received_command(event):
|
|||
is_on = RFX_DEVICES[device_id]._brightness > 0
|
||||
RFX_DEVICES[device_id]._state = is_on
|
||||
RFX_DEVICES[device_id].update_ha_state()
|
||||
else:
|
||||
return
|
||||
|
||||
# Fire event
|
||||
if RFX_DEVICES[device_id].should_fire_event:
|
||||
|
|
|
@ -6,38 +6,50 @@ https://home-assistant.io/components/rollershutter.mqtt/
|
|||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.components.rollershutter import RollershutterDevice
|
||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||
from homeassistant.const import CONF_NAME, CONF_VALUE_TEMPLATE
|
||||
from homeassistant.components.mqtt import (
|
||||
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS)
|
||||
from homeassistant.helpers import template
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_PAYLOAD_UP = 'payload_up'
|
||||
CONF_PAYLOAD_DOWN = 'payload_down'
|
||||
CONF_PAYLOAD_STOP = 'payload_stop'
|
||||
|
||||
DEFAULT_NAME = "MQTT Rollershutter"
|
||||
DEFAULT_QOS = 0
|
||||
DEFAULT_PAYLOAD_UP = "UP"
|
||||
DEFAULT_PAYLOAD_DOWN = "DOWN"
|
||||
DEFAULT_PAYLOAD_STOP = "STOP"
|
||||
|
||||
PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_UP, default=DEFAULT_PAYLOAD_UP): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_DOWN, default=DEFAULT_PAYLOAD_DOWN): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Add MQTT Rollershutter."""
|
||||
if config.get('command_topic') is None:
|
||||
_LOGGER.error("Missing required variable: command_topic")
|
||||
return False
|
||||
|
||||
add_devices_callback([MqttRollershutter(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('state_topic'),
|
||||
config.get('command_topic'),
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
config.get('payload_up', DEFAULT_PAYLOAD_UP),
|
||||
config.get('payload_down', DEFAULT_PAYLOAD_DOWN),
|
||||
config.get('payload_stop', DEFAULT_PAYLOAD_STOP),
|
||||
config.get(CONF_VALUE_TEMPLATE))])
|
||||
config[CONF_NAME],
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config[CONF_COMMAND_TOPIC],
|
||||
config[CONF_QOS],
|
||||
config[CONF_PAYLOAD_UP],
|
||||
config[CONF_PAYLOAD_DOWN],
|
||||
config[CONF_PAYLOAD_STOP],
|
||||
config.get(CONF_VALUE_TEMPLATE)
|
||||
)])
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
|
|
|
@ -67,20 +67,29 @@ def _alias_stripper(validator):
|
|||
return validate
|
||||
|
||||
|
||||
_DELAY_SCHEMA = {
|
||||
vol.Required(CONF_DELAY): vol.All({
|
||||
CONF_ALIAS: cv.string,
|
||||
'days': vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
'seconds': vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
'microseconds': vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
'milliseconds': vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
'minutes': vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
'hours': vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
'weeks': vol.All(vol.Coerce(int), vol.Range(min=0)),
|
||||
}, cv.has_at_least_one_key(
|
||||
'days', 'seconds', 'microseconds', 'milliseconds', 'minutes', 'hours',
|
||||
'weeks'))
|
||||
}
|
||||
_TIMESPEC = vol.Schema({
|
||||
'days': cv.positive_int,
|
||||
'hours': cv.positive_int,
|
||||
'minutes': cv.positive_int,
|
||||
'seconds': cv.positive_int,
|
||||
'milliseconds': cv.positive_int,
|
||||
})
|
||||
_TIMESPEC_REQ = cv.has_at_least_one_key(
|
||||
'days', 'hours', 'minutes', 'seconds', 'milliseconds',
|
||||
)
|
||||
|
||||
_DELAY_SCHEMA = vol.Any(
|
||||
vol.Schema({
|
||||
vol.Required(CONF_DELAY): vol.All(_TIMESPEC.extend({
|
||||
vol.Optional(CONF_ALIAS): cv.string
|
||||
}), _TIMESPEC_REQ)
|
||||
}),
|
||||
# Alternative format in case people forgot to indent after 'delay:'
|
||||
vol.All(_TIMESPEC.extend({
|
||||
vol.Required(CONF_DELAY): None,
|
||||
vol.Optional(CONF_ALIAS): cv.string,
|
||||
}), _TIMESPEC_REQ)
|
||||
)
|
||||
|
||||
_EVENT_SCHEMA = cv.EVENT_SCHEMA.extend({
|
||||
CONF_ALIAS: cv.string,
|
||||
|
@ -234,7 +243,9 @@ class Script(ToggleEntity):
|
|||
self._listener = None
|
||||
self.turn_on()
|
||||
|
||||
delay = timedelta(**action[CONF_DELAY])
|
||||
timespec = action[CONF_DELAY] or action.copy()
|
||||
timespec.pop(CONF_DELAY, None)
|
||||
delay = timedelta(**timespec)
|
||||
self._listener = track_point_in_utc_time(
|
||||
self.hass, script_delay, date_util.utcnow() + delay)
|
||||
self._cur = cur + 1
|
||||
|
|
|
@ -6,33 +6,40 @@ https://home-assistant.io/components/sensor.mqtt/
|
|||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.const import CONF_VALUE_TEMPLATE, STATE_UNKNOWN
|
||||
from homeassistant.const import CONF_NAME, CONF_VALUE_TEMPLATE, STATE_UNKNOWN
|
||||
from homeassistant.components.mqtt import CONF_STATE_TOPIC, CONF_QOS
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers import template
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = "MQTT Sensor"
|
||||
DEFAULT_QOS = 0
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_UNIT_OF_MEASUREMENT = 'unit_of_measurement'
|
||||
|
||||
DEFAULT_NAME = "MQTT Sensor"
|
||||
|
||||
PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||
})
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Setup MQTT Sensor."""
|
||||
if config.get('state_topic') is None:
|
||||
_LOGGER.error("Missing required variable: state_topic")
|
||||
return False
|
||||
|
||||
add_devices_callback([MqttSensor(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('state_topic'),
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
config.get('unit_of_measurement'),
|
||||
config.get(CONF_VALUE_TEMPLATE))])
|
||||
config[CONF_NAME],
|
||||
config[CONF_STATE_TOPIC],
|
||||
config[CONF_QOS],
|
||||
config.get(CONF_UNIT_OF_MEASUREMENT),
|
||||
config.get(CONF_VALUE_TEMPLATE),
|
||||
)])
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
|
|
|
@ -6,13 +6,16 @@ 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
|
||||
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']
|
||||
|
||||
|
@ -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(
|
||||
|
|
|
@ -5,9 +5,10 @@ For more details about this platform, please refer to the documentation at
|
|||
https://home-assistant.io/components/sensor.yr/
|
||||
"""
|
||||
import logging
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
@ -18,6 +19,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
REQUIREMENTS = ['xmltodict']
|
||||
|
||||
CONF_MONITORED_CONDITIONS = 'monitored_conditions'
|
||||
|
||||
# Sensor types are defined like so:
|
||||
SENSOR_TYPES = {
|
||||
'symbol': ['Symbol', None],
|
||||
|
@ -36,6 +39,13 @@ SENSOR_TYPES = {
|
|||
'dewpointTemperature': ['Dewpoint temperature', '°C'],
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = vol.Schema({
|
||||
vol.Required('platform'): 'yr',
|
||||
vol.Optional(CONF_MONITORED_CONDITIONS, default=[]):
|
||||
[vol.In(SENSOR_TYPES.keys())],
|
||||
vol.Optional('elevation'): cv.positive_int,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Yr.no sensor."""
|
||||
|
@ -58,12 +68,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
weather = YrData(coordinates)
|
||||
|
||||
dev = []
|
||||
if 'monitored_conditions' in config:
|
||||
for variable in config['monitored_conditions']:
|
||||
if variable not in SENSOR_TYPES:
|
||||
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(YrSensor(variable, weather))
|
||||
for sensor_type in config[CONF_MONITORED_CONDITIONS]:
|
||||
dev.append(YrSensor(sensor_type, weather))
|
||||
|
||||
# add symbol as default sensor
|
||||
if len(dev) == 0:
|
||||
|
|
|
@ -14,7 +14,7 @@ from homeassistant.helpers.event import (
|
|||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util import location as location_util
|
||||
|
||||
REQUIREMENTS = ['astral==0.9']
|
||||
REQUIREMENTS = ['astral==1.0']
|
||||
DOMAIN = "sun"
|
||||
ENTITY_ID = "sun.sun"
|
||||
|
||||
|
@ -113,8 +113,8 @@ def setup(hass, config):
|
|||
|
||||
from astral import Location
|
||||
|
||||
location = Location(('', '', latitude, longitude, hass.config.time_zone,
|
||||
elevation))
|
||||
location = Location(('', '', latitude, longitude,
|
||||
hass.config.time_zone.zone, elevation))
|
||||
|
||||
sun = Sun(hass, location)
|
||||
sun.point_in_time_listener(dt_util.utcnow())
|
||||
|
|
|
@ -6,41 +6,49 @@ https://home-assistant.io/components/switch.mqtt/
|
|||
"""
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||
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
|
||||
from homeassistant.util import convert
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
CONF_PAYLOAD_ON = 'payload_on'
|
||||
CONF_PAYLOAD_OFF = 'payload_off'
|
||||
|
||||
DEFAULT_NAME = "MQTT Switch"
|
||||
DEFAULT_QOS = 0
|
||||
DEFAULT_PAYLOAD_ON = "ON"
|
||||
DEFAULT_PAYLOAD_OFF = "OFF"
|
||||
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_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
|
||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||
})
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Add MQTT switch."""
|
||||
if config.get('command_topic') is None:
|
||||
_LOGGER.error("Missing required variable: command_topic")
|
||||
return False
|
||||
|
||||
add_devices_callback([MqttSwitch(
|
||||
hass,
|
||||
convert(config.get('name'), str, DEFAULT_NAME),
|
||||
config.get('state_topic'),
|
||||
config.get('command_topic'),
|
||||
convert(config.get('qos'), int, DEFAULT_QOS),
|
||||
convert(config.get('retain'), bool, DEFAULT_RETAIN),
|
||||
convert(config.get('payload_on'), str, DEFAULT_PAYLOAD_ON),
|
||||
convert(config.get('payload_off'), str, DEFAULT_PAYLOAD_OFF),
|
||||
convert(config.get('optimistic'), bool, DEFAULT_OPTIMISTIC),
|
||||
config[CONF_NAME],
|
||||
config.get(CONF_STATE_TOPIC),
|
||||
config[CONF_COMMAND_TOPIC],
|
||||
config[CONF_QOS],
|
||||
config[CONF_RETAIN],
|
||||
config[CONF_PAYLOAD_ON],
|
||||
config[CONF_PAYLOAD_OFF],
|
||||
config[CONF_OPTIMISTIC],
|
||||
config.get(CONF_VALUE_TEMPLATE))])
|
||||
|
||||
|
||||
|
|
|
@ -26,9 +26,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
|||
_LOGGER.error("Missing required variable: host")
|
||||
continue
|
||||
host = switch.get('host')
|
||||
mac = switch.get('mac')
|
||||
try:
|
||||
switches.append(S20Switch(switch.get('name', DEFAULT_NAME),
|
||||
S20(host)))
|
||||
S20(host, mac=mac)))
|
||||
_LOGGER.info("Initialized S20 at %s", host)
|
||||
except S20Exception:
|
||||
_LOGGER.exception("S20 at %s couldn't be initialized",
|
||||
|
|
|
@ -6,6 +6,7 @@ https://home-assistant.io/components/switch.rfxtrx/
|
|||
"""
|
||||
import logging
|
||||
|
||||
|
||||
import homeassistant.components.rfxtrx as rfxtrx
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
|
||||
|
@ -13,6 +14,8 @@ DEPENDENCIES = ['rfxtrx']
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = rfxtrx.DEFAULT_SCHEMA
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Setup the RFXtrx platform."""
|
||||
|
|
|
@ -270,12 +270,12 @@ class ThermostatDevice(Entity):
|
|||
@property
|
||||
def min_temp(self):
|
||||
"""Return the minimum temperature."""
|
||||
return round(convert(7, TEMP_CELCIUS, self.unit_of_measurement))
|
||||
return convert(7, TEMP_CELCIUS, self.unit_of_measurement)
|
||||
|
||||
@property
|
||||
def max_temp(self):
|
||||
"""Return the maximum temperature."""
|
||||
return round(convert(35, TEMP_CELCIUS, self.unit_of_measurement))
|
||||
return convert(35, TEMP_CELCIUS, self.unit_of_measurement)
|
||||
|
||||
def _convert_for_display(self, temp):
|
||||
"""Convert temperature into preferred units for display purposes."""
|
||||
|
|
|
@ -7,12 +7,17 @@ https://home-assistant.io/components/thermostat.homematic/
|
|||
import logging
|
||||
import socket
|
||||
from xmlrpc.client import ServerProxy
|
||||
from xmlrpc.client import Error
|
||||
from collections import namedtuple
|
||||
|
||||
from homeassistant.components.thermostat import ThermostatDevice
|
||||
from homeassistant.const import TEMP_CELCIUS
|
||||
from homeassistant.helpers.temperature import convert
|
||||
|
||||
REQUIREMENTS = []
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_ADDRESS = 'address'
|
||||
CONF_DEVICES = 'devices'
|
||||
CONF_ID = 'id'
|
||||
|
@ -20,29 +25,59 @@ PROPERTY_SET_TEMPERATURE = 'SET_TEMPERATURE'
|
|||
PROPERTY_VALVE_STATE = 'VALVE_STATE'
|
||||
PROPERTY_ACTUAL_TEMPERATURE = 'ACTUAL_TEMPERATURE'
|
||||
PROPERTY_BATTERY_STATE = 'BATTERY_STATE'
|
||||
PROPERTY_LOWBAT = 'LOWBAT'
|
||||
PROPERTY_CONTROL_MODE = 'CONTROL_MODE'
|
||||
PROPERTY_BURST_MODE = 'BURST_RX'
|
||||
TYPE_HM_THERMOSTAT = 'HOMEMATIC_THERMOSTAT'
|
||||
TYPE_HM_WALLTHERMOSTAT = 'HOMEMATIC_WALLTHERMOSTAT'
|
||||
TYPE_MAX_THERMOSTAT = 'MAX_THERMOSTAT'
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
HomematicConfig = namedtuple('HomematicConfig',
|
||||
['device_type',
|
||||
'platform_type',
|
||||
'channel',
|
||||
'maint_channel'])
|
||||
|
||||
HM_TYPE_MAPPING = {
|
||||
'HM-CC-RT-DN': HomematicConfig('HM-CC-RT-DN',
|
||||
TYPE_HM_THERMOSTAT,
|
||||
4, 4),
|
||||
'HM-CC-RT-DN-BoM': HomematicConfig('HM-CC-RT-DN-BoM',
|
||||
TYPE_HM_THERMOSTAT,
|
||||
4, 4),
|
||||
'HM-TC-IT-WM-W-EU': HomematicConfig('HM-TC-IT-WM-W-EU',
|
||||
TYPE_HM_WALLTHERMOSTAT,
|
||||
2, 2),
|
||||
'BC-RT-TRX-CyG': HomematicConfig('BC-RT-TRX-CyG',
|
||||
TYPE_MAX_THERMOSTAT,
|
||||
1, 0),
|
||||
'BC-RT-TRX-CyG-2': HomematicConfig('BC-RT-TRX-CyG-2',
|
||||
TYPE_MAX_THERMOSTAT,
|
||||
1, 0),
|
||||
'BC-RT-TRX-CyG-3': HomematicConfig('BC-RT-TRX-CyG-3',
|
||||
TYPE_MAX_THERMOSTAT,
|
||||
1, 0)
|
||||
}
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Homematic thermostat."""
|
||||
devices = []
|
||||
try:
|
||||
homegear = ServerProxy(config[CONF_ADDRESS])
|
||||
address = config[CONF_ADDRESS]
|
||||
homegear = ServerProxy(address)
|
||||
|
||||
for name, device_cfg in config[CONF_DEVICES].items():
|
||||
# get device description to detect the type
|
||||
device_type = homegear.getDeviceDescription(
|
||||
device_cfg[CONF_ID] + ':-1')['TYPE']
|
||||
|
||||
if device_type in ['HM-CC-RT-DN', 'HM-CC-RT-DN-BoM']:
|
||||
devices.append(HomematicThermostat(homegear,
|
||||
device_cfg[CONF_ID],
|
||||
name, 4))
|
||||
elif device_type == 'HM-TC-IT-WM-W-EU':
|
||||
devices.append(HomematicThermostat(homegear,
|
||||
device_cfg[CONF_ID],
|
||||
name, 2))
|
||||
if device_type in HM_TYPE_MAPPING.keys():
|
||||
devices.append(HomematicThermostat(
|
||||
HM_TYPE_MAPPING[device_type],
|
||||
address,
|
||||
device_cfg[CONF_ID],
|
||||
name))
|
||||
else:
|
||||
raise ValueError(
|
||||
"Device Type '{}' currently not supported".format(
|
||||
|
@ -60,14 +95,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
class HomematicThermostat(ThermostatDevice):
|
||||
"""Representation of a Homematic thermostat."""
|
||||
|
||||
def __init__(self, device, _id, name, channel):
|
||||
def __init__(self, hm_config, address, _id, name):
|
||||
"""Initialize the thermostat."""
|
||||
self.device = device
|
||||
self._hm_config = hm_config
|
||||
self.address = address
|
||||
self._id = _id
|
||||
self._channel = channel
|
||||
self._name = name
|
||||
self._full_device_name = '{}:{}'.format(self._id, self._channel)
|
||||
|
||||
self._full_device_name = '{}:{}'.format(self._id,
|
||||
self._hm_config.channel)
|
||||
self._maint_device_name = '{}:{}'.format(self._id,
|
||||
self._hm_config.maint_channel)
|
||||
self._current_temperature = None
|
||||
self._target_temperature = None
|
||||
self._valve = None
|
||||
|
@ -97,9 +134,10 @@ class HomematicThermostat(ThermostatDevice):
|
|||
|
||||
def set_temperature(self, temperature):
|
||||
"""Set new target temperature."""
|
||||
self.device.setValue(self._full_device_name,
|
||||
PROPERTY_SET_TEMPERATURE,
|
||||
temperature)
|
||||
device = ServerProxy(self.address)
|
||||
device.setValue(self._full_device_name,
|
||||
PROPERTY_SET_TEMPERATURE,
|
||||
temperature)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
@ -108,21 +146,49 @@ class HomematicThermostat(ThermostatDevice):
|
|||
"battery": self._battery,
|
||||
"mode": self._mode}
|
||||
|
||||
@property
|
||||
def min_temp(self):
|
||||
"""Return the minimum temperature - 4.5 means off."""
|
||||
return convert(4.5, TEMP_CELCIUS, self.unit_of_measurement)
|
||||
|
||||
@property
|
||||
def max_temp(self):
|
||||
"""Return the maximum temperature - 30.5 means on."""
|
||||
return convert(30.5, TEMP_CELCIUS, self.unit_of_measurement)
|
||||
|
||||
def update(self):
|
||||
"""Update the data from the thermostat."""
|
||||
try:
|
||||
self._current_temperature = self.device.getValue(
|
||||
device = ServerProxy(self.address)
|
||||
self._current_temperature = device.getValue(
|
||||
self._full_device_name,
|
||||
PROPERTY_ACTUAL_TEMPERATURE)
|
||||
self._target_temperature = self.device.getValue(
|
||||
self._target_temperature = device.getValue(
|
||||
self._full_device_name,
|
||||
PROPERTY_SET_TEMPERATURE)
|
||||
self._valve = self.device.getValue(self._full_device_name,
|
||||
PROPERTY_VALVE_STATE)
|
||||
self._battery = self.device.getValue(self._full_device_name,
|
||||
PROPERTY_BATTERY_STATE)
|
||||
self._mode = self.device.getValue(self._full_device_name,
|
||||
PROPERTY_CONTROL_MODE)
|
||||
except socket.error:
|
||||
self._valve = device.getValue(
|
||||
self._full_device_name,
|
||||
PROPERTY_VALVE_STATE)
|
||||
self._mode = device.getValue(
|
||||
self._full_device_name,
|
||||
PROPERTY_CONTROL_MODE)
|
||||
|
||||
if self._hm_config.platform_type in [TYPE_HM_THERMOSTAT,
|
||||
TYPE_HM_WALLTHERMOSTAT]:
|
||||
self._battery = device.getValue(self._maint_device_name,
|
||||
PROPERTY_BATTERY_STATE)
|
||||
elif self._hm_config.platform_type == TYPE_MAX_THERMOSTAT:
|
||||
# emulate homematic battery voltage,
|
||||
# max reports lowbat if voltage < 2.2V
|
||||
# while homematic battery_state should
|
||||
# be between 1.5V and 4.6V
|
||||
lowbat = device.getValue(self._maint_device_name,
|
||||
PROPERTY_LOWBAT)
|
||||
if lowbat:
|
||||
self._battery = 1.5
|
||||
else:
|
||||
self._battery = 4.6
|
||||
|
||||
except Error:
|
||||
_LOGGER.exception("Did not receive any temperature data from the "
|
||||
"homematic API.")
|
||||
|
|
|
@ -70,7 +70,7 @@ class NestThermostat(ThermostatDevice):
|
|||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return round(self.device.temperature, 1)
|
||||
return self.device.temperature
|
||||
|
||||
@property
|
||||
def operation(self):
|
||||
|
@ -102,21 +102,21 @@ class NestThermostat(ThermostatDevice):
|
|||
else:
|
||||
temp = target
|
||||
|
||||
return round(temp, 1)
|
||||
return temp
|
||||
|
||||
@property
|
||||
def target_temperature_low(self):
|
||||
"""Return the lower bound temperature we try to reach."""
|
||||
if self.device.mode == 'range':
|
||||
return round(self.device.target[0], 1)
|
||||
return round(self.target_temperature, 1)
|
||||
return self.device.target[0]
|
||||
return self.target_temperature
|
||||
|
||||
@property
|
||||
def target_temperature_high(self):
|
||||
"""Return the upper bound temperature we try to reach."""
|
||||
if self.device.mode == 'range':
|
||||
return round(self.device.target[1], 1)
|
||||
return round(self.target_temperature, 1)
|
||||
return self.device.target[1]
|
||||
return self.target_temperature
|
||||
|
||||
@property
|
||||
def is_away_mode_on(self):
|
||||
|
|
|
@ -80,7 +80,7 @@ class RadioThermostat(ThermostatDevice):
|
|||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return round(self._current_temperature, 1)
|
||||
return self._current_temperature
|
||||
|
||||
@property
|
||||
def operation(self):
|
||||
|
@ -90,7 +90,7 @@ class RadioThermostat(ThermostatDevice):
|
|||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the temperature we try to reach."""
|
||||
return round(self._target_temperature, 1)
|
||||
return self._target_temperature
|
||||
|
||||
def update(self):
|
||||
"""Update the data from the thermostat."""
|
||||
|
|
|
@ -20,6 +20,11 @@ ENTITY_ID = 'updater.updater'
|
|||
|
||||
def setup(hass, config):
|
||||
"""Setup the updater component."""
|
||||
if 'dev' in CURRENT_VERSION:
|
||||
# This component only makes sense in release versions
|
||||
_LOGGER.warning('Updater not supported in development version')
|
||||
return False
|
||||
|
||||
def check_newest_version(_=None):
|
||||
"""Check if a new version is available and report if one is."""
|
||||
newest = get_newest_version()
|
||||
|
|
|
@ -109,7 +109,7 @@ def setup(hass, base_config):
|
|||
(LIGHT, DISCOVER_LIGHTS),
|
||||
(SWITCH, DISCOVER_SWITCHES))):
|
||||
component = get_component(comp_name)
|
||||
bootstrap.setup_component(hass, component.DOMAIN, config)
|
||||
bootstrap.setup_component(hass, component.DOMAIN, base_config)
|
||||
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
|
||||
{ATTR_SERVICE: discovery,
|
||||
ATTR_DISCOVERED: {}})
|
||||
|
|
|
@ -9,7 +9,7 @@ import logging
|
|||
from homeassistant.components import discovery
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
|
||||
REQUIREMENTS = ['pywemo==0.3.24']
|
||||
REQUIREMENTS = ['pywemo==0.4.1']
|
||||
|
||||
DOMAIN = 'wemo'
|
||||
DISCOVER_LIGHTS = 'wemo.light'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# coding: utf-8
|
||||
"""Constants used by Home Assistant components."""
|
||||
|
||||
__version__ = "0.17.0.dev0"
|
||||
__version__ = "0.18.0.dev0"
|
||||
REQUIRED_PYTHON_VER = (3, 4)
|
||||
|
||||
PLATFORM_FORMAT = '{}.{}'
|
||||
|
@ -23,12 +23,14 @@ CONF_CUSTOMIZE = "customize"
|
|||
|
||||
CONF_PLATFORM = "platform"
|
||||
CONF_HOST = "host"
|
||||
CONF_PORT = "port"
|
||||
CONF_HOSTS = "hosts"
|
||||
CONF_USERNAME = "username"
|
||||
CONF_PASSWORD = "password"
|
||||
CONF_API_KEY = "api_key"
|
||||
CONF_ACCESS_TOKEN = "access_token"
|
||||
CONF_FILENAME = "filename"
|
||||
CONF_OPTIMISTIC = 'optimistic'
|
||||
CONF_SCAN_INTERVAL = "scan_interval"
|
||||
CONF_VALUE_TEMPLATE = "value_template"
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ from homeassistant.util import slugify
|
|||
# Home Assistant types
|
||||
byte = vol.All(vol.Coerce(int), vol.Range(min=0, max=255))
|
||||
small_float = vol.All(vol.Coerce(float), vol.Range(min=0, max=1))
|
||||
positive_int = vol.All(vol.Coerce(int), vol.Range(min=0))
|
||||
latitude = vol.All(vol.Coerce(float), vol.Range(min=-90, max=90),
|
||||
msg='invalid latitude')
|
||||
longitude = vol.All(vol.Coerce(float), vol.Range(min=-180, max=180),
|
||||
|
@ -34,6 +35,11 @@ def boolean(value):
|
|||
return bool(value)
|
||||
|
||||
|
||||
def isfile(value):
|
||||
"""Validate that the value is an existing file."""
|
||||
return vol.IsFile('not a file')(value)
|
||||
|
||||
|
||||
def ensure_list(value):
|
||||
"""Wrap value in list if it is not one."""
|
||||
return value if isinstance(value, list) else [value]
|
||||
|
@ -41,6 +47,7 @@ def ensure_list(value):
|
|||
|
||||
def entity_id(value):
|
||||
"""Validate Entity ID."""
|
||||
value = string(value).lower()
|
||||
if valid_entity_id(value):
|
||||
return value
|
||||
raise vol.Invalid('Entity ID {} does not match format <domain>.<object_id>'
|
||||
|
@ -52,10 +59,7 @@ def entity_ids(value):
|
|||
if isinstance(value, str):
|
||||
value = [ent_id.strip() for ent_id in value.split(',')]
|
||||
|
||||
for ent_id in value:
|
||||
entity_id(ent_id)
|
||||
|
||||
return value
|
||||
return [entity_id(ent_id) for ent_id in value]
|
||||
|
||||
|
||||
def icon(value):
|
||||
|
@ -188,7 +192,6 @@ def key_dependency(key, dependency):
|
|||
"""Test dependencies."""
|
||||
if not isinstance(value, dict):
|
||||
raise vol.Invalid('key dependencies require a dict')
|
||||
print(key, value)
|
||||
if key in value and dependency not in value:
|
||||
raise vol.Invalid('dependency violation - key "{}" requires '
|
||||
'key "{}" to exist'.format(key, dependency))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""YAML utility functions."""
|
||||
import logging
|
||||
import os
|
||||
from collections import Counter, OrderedDict
|
||||
from collections import OrderedDict
|
||||
|
||||
import yaml
|
||||
|
||||
|
@ -10,17 +10,28 @@ from homeassistant.exceptions import HomeAssistantError
|
|||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable=too-many-ancestors
|
||||
class SafeLineLoader(yaml.SafeLoader):
|
||||
"""Loader class that keeps track of line numbers."""
|
||||
|
||||
def compose_node(self, parent, index):
|
||||
"""Annotate a node with the first line it was seen."""
|
||||
last_line = self.line
|
||||
node = super(SafeLineLoader, self).compose_node(parent, index)
|
||||
node.__line__ = last_line + 1
|
||||
return node
|
||||
|
||||
|
||||
def load_yaml(fname):
|
||||
"""Load a YAML file."""
|
||||
try:
|
||||
with open(fname, encoding='utf-8') as conf_file:
|
||||
# If configuration file is empty YAML returns None
|
||||
# We convert that to an empty dict
|
||||
return yaml.safe_load(conf_file) or {}
|
||||
except yaml.YAMLError:
|
||||
error = 'Error reading YAML configuration file {}'.format(fname)
|
||||
_LOGGER.exception(error)
|
||||
raise HomeAssistantError(error)
|
||||
return yaml.load(conf_file, Loader=SafeLineLoader) or {}
|
||||
except yaml.YAMLError as exc:
|
||||
_LOGGER.error(exc)
|
||||
raise HomeAssistantError(exc)
|
||||
|
||||
|
||||
def _include_yaml(loader, node):
|
||||
|
@ -37,11 +48,21 @@ def _ordered_dict(loader, node):
|
|||
"""Load YAML mappings into an ordered dict to preserve key order."""
|
||||
loader.flatten_mapping(node)
|
||||
nodes = loader.construct_pairs(node)
|
||||
dups = [k for k, v in Counter(k for k, _ in nodes).items() if v > 1]
|
||||
if dups:
|
||||
raise yaml.YAMLError("ERROR: duplicate keys: {}".format(dups))
|
||||
return OrderedDict(nodes)
|
||||
|
||||
seen = {}
|
||||
for (key, _), (node, _) in zip(nodes, node.value):
|
||||
line = getattr(node, '__line__', 'unknown')
|
||||
if key in seen:
|
||||
fname = getattr(loader.stream, 'name', '')
|
||||
first_mark = yaml.Mark(fname, 0, seen[key], -1, None, None)
|
||||
second_mark = yaml.Mark(fname, 0, line, -1, None, None)
|
||||
raise yaml.MarkedYAMLError(
|
||||
context="duplicate key: \"{}\"".format(key),
|
||||
context_mark=first_mark, problem_mark=second_mark,
|
||||
)
|
||||
seen[key] = line
|
||||
|
||||
return OrderedDict(nodes)
|
||||
|
||||
yaml.SafeLoader.add_constructor('!include', _include_yaml)
|
||||
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
||||
|
|
|
@ -26,7 +26,7 @@ TwitterAPI==2.3.6
|
|||
apcaccess==0.0.4
|
||||
|
||||
# homeassistant.components.sun
|
||||
astral==0.9
|
||||
astral==1.0
|
||||
|
||||
# homeassistant.components.light.blinksticklight
|
||||
blinkstick==1.1.7
|
||||
|
@ -132,7 +132,7 @@ messagebird==1.1.1
|
|||
mficlient==0.3.0
|
||||
|
||||
# homeassistant.components.discovery
|
||||
netdisco==0.6.1
|
||||
netdisco==0.6.2
|
||||
|
||||
# homeassistant.components.sensor.neurio_energy
|
||||
neurio==0.2.10
|
||||
|
@ -189,7 +189,7 @@ pyicloud==0.7.2
|
|||
pyloopenergy==0.0.7
|
||||
|
||||
# homeassistant.components.device_tracker.netgear
|
||||
pynetgear==0.3.2
|
||||
pynetgear==0.3.3
|
||||
|
||||
# homeassistant.components.alarm_control_panel.nx584
|
||||
# homeassistant.components.binary_sensor.nx584
|
||||
|
@ -241,7 +241,7 @@ pyuserinput==0.1.9
|
|||
pyvera==0.2.10
|
||||
|
||||
# homeassistant.components.wemo
|
||||
pywemo==0.3.24
|
||||
pywemo==0.4.1
|
||||
|
||||
# homeassistant.components.thermostat.radiotherm
|
||||
radiotherm==1.2
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# This is a simple service file for systems with systemd to tun HA as user.
|
||||
#
|
||||
# For details please check https://home-assistant.io/getting-started/autostart/
|
||||
#
|
||||
[Unit]
|
||||
Description=Home Assistant for %i
|
||||
After=network.target
|
||||
|
@ -9,6 +11,7 @@ Type=simple
|
|||
User=%i
|
||||
# Enable the following line if you get network-related HA errors during boot
|
||||
#ExecStartPre=/usr/bin/sleep 60
|
||||
# Use `whereis hass` to determine the path of hass
|
||||
ExecStart=/usr/bin/hass
|
||||
SendSIGKILL=no
|
||||
|
||||
|
|
|
@ -4,11 +4,13 @@ from datetime import timedelta
|
|||
from unittest import mock
|
||||
|
||||
from homeassistant import core as ha, loader
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
import homeassistant.util.dt as date_util
|
||||
from homeassistant.const import (
|
||||
STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME, EVENT_TIME_CHANGED,
|
||||
EVENT_STATE_CHANGED, EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE,
|
||||
ATTR_DISCOVERED, SERVER_PORT)
|
||||
ATTR_DISCOVERED, SERVER_PORT, TEMP_CELCIUS)
|
||||
from homeassistant.components import sun, mqtt
|
||||
|
||||
_TEST_INSTANCE_PORT = SERVER_PORT
|
||||
|
@ -33,6 +35,8 @@ def get_test_home_assistant(num_threads=None):
|
|||
hass.config.config_dir = get_test_config_dir()
|
||||
hass.config.latitude = 32.87336
|
||||
hass.config.longitude = -117.22743
|
||||
hass.config.time_zone = date_util.get_time_zone('US/Pacific')
|
||||
hass.config.temperature_unit = TEMP_CELCIUS
|
||||
|
||||
if 'custom_components.test' not in loader.AVAILABLE_COMPONENTS:
|
||||
loader.prepare(hass)
|
||||
|
@ -123,12 +127,11 @@ def mock_http_component(hass):
|
|||
@mock.patch('homeassistant.components.mqtt.MQTT')
|
||||
def mock_mqtt_component(hass, mock_mqtt):
|
||||
"""Mock the MQTT component."""
|
||||
mqtt.setup(hass, {
|
||||
_setup_component(hass, mqtt.DOMAIN, {
|
||||
mqtt.DOMAIN: {
|
||||
mqtt.CONF_BROKER: 'mock-broker',
|
||||
}
|
||||
})
|
||||
hass.config.components.append(mqtt.DOMAIN)
|
||||
return mock_mqtt
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""The tests the MQTT alarm control panel component."""
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.const import (
|
||||
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
|
||||
|
@ -25,37 +25,37 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
"""Stop down stuff we started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch('homeassistant.components.alarm_control_panel.mqtt._LOGGER.error')
|
||||
def test_fail_setup_without_state_topic(self, mock_error):
|
||||
def test_fail_setup_without_state_topic(self):
|
||||
"""Test for failing with no state topic."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert not _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'command_topic': 'alarm/command'
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
self.assertEqual(1, mock_error.call_count)
|
||||
|
||||
@patch('homeassistant.components.alarm_control_panel.mqtt._LOGGER.error')
|
||||
def test_fail_setup_without_command_topic(self, mock_error):
|
||||
def test_fail_setup_without_command_topic(self):
|
||||
"""Test failing with no command topic."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert not _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'state_topic': 'alarm/state'
|
||||
}}))
|
||||
|
||||
self.assertEqual(1, mock_error.call_count)
|
||||
}
|
||||
})
|
||||
|
||||
def test_update_state_via_state_topic(self):
|
||||
"""Test updating with via state topic."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
entity_id = 'alarm_control_panel.test'
|
||||
|
||||
|
@ -71,13 +71,15 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_ignore_update_state_if_unknown_via_state_topic(self):
|
||||
"""Test ignoring updates via state topic."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
entity_id = 'alarm_control_panel.test'
|
||||
|
||||
|
@ -90,13 +92,15 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_home_publishes_mqtt(self):
|
||||
"""Test publishing of MQTT messages while armed."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
alarm_control_panel.alarm_arm_home(self.hass)
|
||||
self.hass.pool.block_till_done()
|
||||
|
@ -105,14 +109,16 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_home_not_publishes_mqtt_with_invalid_code(self):
|
||||
"""Test not publishing of MQTT messages with invalid code."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
'code': '1234'
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
call_count = self.mock_publish.call_count
|
||||
alarm_control_panel.alarm_arm_home(self.hass, 'abcd')
|
||||
|
@ -121,13 +127,15 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_away_publishes_mqtt(self):
|
||||
"""Test publishing of MQTT messages while armed."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
alarm_control_panel.alarm_arm_away(self.hass)
|
||||
self.hass.pool.block_till_done()
|
||||
|
@ -136,14 +144,16 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_arm_away_not_publishes_mqtt_with_invalid_code(self):
|
||||
"""Test not publishing of MQTT messages with invalid code."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
'code': '1234'
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
call_count = self.mock_publish.call_count
|
||||
alarm_control_panel.alarm_arm_away(self.hass, 'abcd')
|
||||
|
@ -152,13 +162,15 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_disarm_publishes_mqtt(self):
|
||||
"""Test publishing of MQTT messages while disarmed."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
alarm_control_panel.alarm_disarm(self.hass)
|
||||
self.hass.pool.block_till_done()
|
||||
|
@ -167,14 +179,16 @@ class TestAlarmControlPanelMQTT(unittest.TestCase):
|
|||
|
||||
def test_disarm_not_publishes_mqtt_with_invalid_code(self):
|
||||
"""Test not publishing of MQTT messages with invalid code."""
|
||||
self.assertTrue(alarm_control_panel.setup(self.hass, {
|
||||
'alarm_control_panel': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, alarm_control_panel.DOMAIN, {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'alarm/state',
|
||||
'command_topic': 'alarm/command',
|
||||
'code': '1234'
|
||||
}}))
|
||||
}
|
||||
})
|
||||
|
||||
call_count = self.mock_publish.call_count
|
||||
alarm_control_panel.alarm_disarm(self.hass, 'abcd')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""The tests for the MQTT binary sensor platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
import homeassistant.components.binary_sensor as binary_sensor
|
||||
from tests.common import mock_mqtt_component, fire_mqtt_message
|
||||
from homeassistant.const import (STATE_OFF, STATE_ON)
|
||||
|
@ -22,15 +23,16 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_setting_sensor_value_via_mqtt_message(self):
|
||||
"""Test the setting of the value via MQTT."""
|
||||
self.assertTrue(binary_sensor.setup(self.hass, {
|
||||
'binary_sensor': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, binary_sensor.DOMAIN, {
|
||||
binary_sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test-topic',
|
||||
'payload_on': 'ON',
|
||||
'payload_off': 'OFF',
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('binary_sensor.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -47,28 +49,30 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_valid_sensor_class(self):
|
||||
"""Test the setting of a valid sensor class."""
|
||||
self.assertTrue(binary_sensor.setup(self.hass, {
|
||||
'binary_sensor': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, binary_sensor.DOMAIN, {
|
||||
binary_sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'sensor_class': 'motion',
|
||||
'state_topic': 'test-topic',
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('binary_sensor.test')
|
||||
self.assertEqual('motion', state.attributes.get('sensor_class'))
|
||||
|
||||
def test_invalid_sensor_class(self):
|
||||
"""Test the setting of an invalid sensor class."""
|
||||
self.assertTrue(binary_sensor.setup(self.hass, {
|
||||
'binary_sensor': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, binary_sensor.DOMAIN, {
|
||||
binary_sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'sensor_class': 'abc123',
|
||||
'state_topic': 'test-topic',
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('binary_sensor.test')
|
||||
self.assertIsNone(state.attributes.get('sensor_class'))
|
||||
|
|
|
@ -197,3 +197,16 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
|||
mock_see.assert_called_once_with(
|
||||
mac=mac, dev_id=dev_id, host_name=host_name,
|
||||
location_name=location_name, gps=gps)
|
||||
|
||||
def test_not_write_duplicate_yaml_keys(self):
|
||||
"""Test that the device tracker will not generate invalid YAML."""
|
||||
self.assertTrue(device_tracker.setup(self.hass, {}))
|
||||
|
||||
device_tracker.see(self.hass, 'mac_1', host_name='hello')
|
||||
device_tracker.see(self.hass, 'mac_2', host_name='hello')
|
||||
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
config = device_tracker.load_config(self.yaml_devices, self.hass,
|
||||
timedelta(seconds=0), 0)
|
||||
assert len(config) == 2
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import unittest
|
||||
import os
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.components import device_tracker
|
||||
from homeassistant.const import CONF_PLATFORM
|
||||
|
||||
|
@ -31,11 +32,13 @@ class TestComponentsDeviceTrackerMQTT(unittest.TestCase):
|
|||
topic = '/location/paulus'
|
||||
location = 'work'
|
||||
|
||||
self.assertTrue(device_tracker.setup(self.hass, {
|
||||
self.hass.config.components = ['mqtt', 'zone']
|
||||
assert _setup_component(self.hass, device_tracker.DOMAIN, {
|
||||
device_tracker.DOMAIN: {
|
||||
CONF_PLATFORM: 'mqtt',
|
||||
'devices': {dev_id: topic}
|
||||
}}))
|
||||
}
|
||||
})
|
||||
fire_mqtt_message(self.hass, topic, location)
|
||||
self.hass.pool.block_till_done()
|
||||
self.assertEqual(location, self.hass.states.get(enttiy_id).state)
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
"""The tests for the MQTT Garge door platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.const import STATE_OPEN, STATE_CLOSED, ATTR_ASSUMED_STATE
|
||||
|
||||
import homeassistant.components.garage_door as garage_door
|
||||
from tests.common import (
|
||||
mock_mqtt_component, fire_mqtt_message, get_test_home_assistant)
|
||||
|
||||
|
||||
class TestGarageDoorMQTT(unittest.TestCase):
|
||||
"""Test the MQTT Garage door."""
|
||||
|
||||
def setUp(self): # pylint: disable=invalid-name
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.mock_publish = mock_mqtt_component(self.hass)
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
""""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_fail_setup_if_no_command_topic(self):
|
||||
"""Test if command fails with command topic."""
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert not _setup_component(self.hass, garage_door.DOMAIN, {
|
||||
garage_door.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': '/home/garage_door/door'
|
||||
}
|
||||
})
|
||||
self.assertIsNone(self.hass.states.get('garage_door.test'))
|
||||
|
||||
def test_controlling_state_via_topic(self):
|
||||
"""Test the controlling state via topic."""
|
||||
assert _setup_component(self.hass, garage_door.DOMAIN, {
|
||||
garage_door.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'state_open': 1,
|
||||
'state_closed': 0,
|
||||
'service_open': 1,
|
||||
'service_close': 0
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_CLOSED, state.state)
|
||||
self.assertIsNone(state.attributes.get(ATTR_ASSUMED_STATE))
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '1')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_OPEN, state.state)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '0')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_CLOSED, state.state)
|
||||
|
||||
def test_sending_mqtt_commands_and_optimistic(self):
|
||||
"""Test the sending MQTT commands in optimistic mode."""
|
||||
assert _setup_component(self.hass, garage_door.DOMAIN, {
|
||||
garage_door.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'command_topic': 'command-topic',
|
||||
'state_open': 'beer state open',
|
||||
'state_closed': 'beer state closed',
|
||||
'service_open': 'beer open',
|
||||
'service_close': 'beer close',
|
||||
'qos': '2'
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_CLOSED, state.state)
|
||||
self.assertTrue(state.attributes.get(ATTR_ASSUMED_STATE))
|
||||
|
||||
garage_door.open_door(self.hass, 'garage_door.test')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
self.assertEqual(('command-topic', 'beer open', 2, False),
|
||||
self.mock_publish.mock_calls[-1][1])
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_OPEN, state.state)
|
||||
|
||||
garage_door.close_door(self.hass, 'garage_door.test')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
self.assertEqual(('command-topic', 'beer close', 2, False),
|
||||
self.mock_publish.mock_calls[-1][1])
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_CLOSED, state.state)
|
||||
|
||||
def test_controlling_state_via_topic_and_json_message(self):
|
||||
"""Test the controlling state via topic and JSON message."""
|
||||
assert _setup_component(self.hass, garage_door.DOMAIN, {
|
||||
garage_door.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'state_open': 'beer open',
|
||||
'state_closed': 'beer closed',
|
||||
'service_open': 'beer service open',
|
||||
'service_close': 'beer service close',
|
||||
'value_template': '{{ value_json.val }}'
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_CLOSED, state.state)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '{"val":"beer open"}')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_OPEN, state.state)
|
||||
|
||||
fire_mqtt_message(self.hass, 'state-topic', '{"val":"beer closed"}')
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
state = self.hass.states.get('garage_door.test')
|
||||
self.assertEqual(STATE_CLOSED, state.state)
|
|
@ -58,6 +58,7 @@ light:
|
|||
"""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ASSUMED_STATE
|
||||
import homeassistant.components.light as light
|
||||
from tests.common import (
|
||||
|
@ -78,24 +79,26 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_fail_setup_if_no_command_topic(self):
|
||||
"""Test if command fails with command topic."""
|
||||
self.assertTrue(light.setup(self.hass, {
|
||||
'light': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert not _setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
}
|
||||
}))
|
||||
})
|
||||
self.assertIsNone(self.hass.states.get('light.test'))
|
||||
|
||||
def test_no_color_or_brightness_if_no_topics(self):
|
||||
"""Test if there is no color and brightness if no topic."""
|
||||
self.assertTrue(light.setup(self.hass, {
|
||||
'light': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test_light_rgb/status',
|
||||
'command_topic': 'test_light_rgb/set',
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -112,8 +115,9 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic(self):
|
||||
"""Test the controlling of the state via topic."""
|
||||
self.assertTrue(light.setup(self.hass, {
|
||||
'light': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test_light_rgb/status',
|
||||
|
@ -126,7 +130,7 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'payload_on': 1,
|
||||
'payload_off': 0
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -172,8 +176,9 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_scale(self):
|
||||
"""Test the controlling scale."""
|
||||
self.assertTrue(light.setup(self.hass, {
|
||||
'light': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test_scale/status',
|
||||
|
@ -185,7 +190,7 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'payload_on': 'on',
|
||||
'payload_off': 'off'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -218,8 +223,9 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic_with_templates(self):
|
||||
"""Test the setting og the state with a template."""
|
||||
self.assertTrue(light.setup(self.hass, {
|
||||
'light': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test_light_rgb/status',
|
||||
|
@ -230,7 +236,7 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'brightness_value_template': '{{ value_json.hello }}',
|
||||
'rgb_value_template': '{{ value_json.hello | join(",") }}',
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -252,8 +258,9 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_sending_mqtt_commands_and_optimistic(self):
|
||||
"""Test the sending of command in optimistic mode."""
|
||||
self.assertTrue(light.setup(self.hass, {
|
||||
'light': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'command_topic': 'test_light_rgb/set',
|
||||
|
@ -263,7 +270,7 @@ class TestLightMQTT(unittest.TestCase):
|
|||
'payload_on': 'on',
|
||||
'payload_off': 'off'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -310,15 +317,16 @@ class TestLightMQTT(unittest.TestCase):
|
|||
|
||||
def test_show_brightness_if_only_command_topic(self):
|
||||
"""Test the brightness if only a command topic is present."""
|
||||
self.assertTrue(light.setup(self.hass, {
|
||||
'light': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'brightness_command_topic': 'test_light_rgb/brightness/set',
|
||||
'command_topic': 'test_light_rgb/set',
|
||||
'state_topic': 'test_light_rgb/status',
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
"""The tests for the Rfxtrx light platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.components import rfxtrx as rfxtrx_core
|
||||
from homeassistant.components.light import rfxtrx
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
|
@ -14,6 +13,7 @@ class TestLightRfxtrx(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant(0)
|
||||
self.hass.config.components = ['rfxtrx']
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
@ -23,40 +23,60 @@ class TestLightRfxtrx(unittest.TestCase):
|
|||
rfxtrx_core.RFXOBJECT.close_connection()
|
||||
self.hass.stop()
|
||||
|
||||
def test_valid_config(self):
|
||||
"""Test configuration."""
|
||||
self.assertTrue(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices':
|
||||
{'213c7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51',
|
||||
rfxtrx_core.ATTR_FIREEVENT: True}}}}))
|
||||
|
||||
self.assertTrue(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices':
|
||||
{'213c7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51',
|
||||
'signal_repetitions': 3}}}}))
|
||||
|
||||
def test_invalid_config(self):
|
||||
"""Test configuration."""
|
||||
self.assertFalse(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'invalid_key': 'afda',
|
||||
'devices':
|
||||
{'213c7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51',
|
||||
rfxtrx_core.ATTR_FIREEVENT: True}}}}))
|
||||
|
||||
def test_default_config(self):
|
||||
"""Test with 0 lights."""
|
||||
config = {'devices': {}}
|
||||
devices = []
|
||||
"""Test with 0 switches."""
|
||||
self.assertTrue(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'devices': {}}}))
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
def test_one_sensor(self):
|
||||
def test_one_light(self):
|
||||
"""Test with 1 light."""
|
||||
config = {'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51'}}}
|
||||
self.assertTrue(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51'}}}}))
|
||||
|
||||
import RFXtrx as rfxtrxmod
|
||||
rfxtrx_core.RFXOBJECT =\
|
||||
rfxtrxmod.Core("", transport_protocol=rfxtrxmod.DummyTransport)
|
||||
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
|
||||
self.assertEqual(1, len(devices))
|
||||
entity = devices[0]
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
entity = rfxtrx_core.RFX_DEVICES['123efab1']
|
||||
self.assertEqual('Test', entity.name)
|
||||
self.assertEqual('off', entity.state)
|
||||
self.assertTrue(entity.assumed_state)
|
||||
|
@ -65,62 +85,47 @@ class TestLightRfxtrx(unittest.TestCase):
|
|||
self.assertFalse(entity.should_poll)
|
||||
|
||||
self.assertFalse(entity.is_on)
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
entity.turn_on()
|
||||
|
||||
entity.turn_on()
|
||||
self.assertTrue(entity.is_on)
|
||||
self.assertEqual(entity.brightness, 255)
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
entity.turn_off()
|
||||
|
||||
entity.turn_off()
|
||||
self.assertFalse(entity.is_on)
|
||||
self.assertEqual(entity.brightness, 0)
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
entity.turn_on(brightness=100)
|
||||
|
||||
entity.turn_on(brightness=100)
|
||||
self.assertTrue(entity.is_on)
|
||||
self.assertEqual(entity.brightness, 100)
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
entity.turn_on(brightness=10)
|
||||
|
||||
entity.turn_on(brightness=10)
|
||||
self.assertTrue(entity.is_on)
|
||||
self.assertEqual(entity.brightness, 10)
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
entity.turn_on(brightness=255)
|
||||
|
||||
entity.turn_on(brightness=255)
|
||||
self.assertTrue(entity.is_on)
|
||||
self.assertEqual(entity.brightness, 255)
|
||||
|
||||
def test_several_lights(self):
|
||||
"""Test with 3 lights."""
|
||||
config = {'signal_repetitions': 3,
|
||||
'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f230010f71'},
|
||||
'118cdea2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0b1100100118cdea02010f70'},
|
||||
'213c7f216': {
|
||||
'name': 'Living',
|
||||
'packetid': '2b1121cd1213c7f211111f71'}}}
|
||||
devices = []
|
||||
self.assertTrue(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'signal_repetitions': 3,
|
||||
'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f230010f71'},
|
||||
'118cdea2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0b1100100118cdea02010f70'},
|
||||
'213c7f216': {
|
||||
'name': 'Living',
|
||||
'packetid': '0b1100100118cdea02010f70'}}}}))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
|
||||
self.assertEqual(3, len(devices))
|
||||
self.assertEqual(3, len(rfxtrx_core.RFX_DEVICES))
|
||||
device_num = 0
|
||||
for entity in devices:
|
||||
for id in rfxtrx_core.RFX_DEVICES:
|
||||
entity = rfxtrx_core.RFX_DEVICES[id]
|
||||
self.assertEqual(entity.signal_repetitions, 3)
|
||||
if entity.name == 'Living':
|
||||
device_num = device_num + 1
|
||||
|
@ -139,47 +144,33 @@ class TestLightRfxtrx(unittest.TestCase):
|
|||
|
||||
def test_discover_light(self):
|
||||
"""Test with discovery of lights."""
|
||||
config = {'automatic_add': True, 'devices': {}}
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertTrue(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices': {}}}))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b11009e00e6116202020070')
|
||||
event.data = bytearray(b'\x0b\x11\x00\x9e\x00\xe6\x11b\x02\x02\x00p')
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = devices[0]
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = rfxtrx_core.RFX_DEVICES['0e611622']
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(1, len(devices))
|
||||
self.assertEqual('<Entity 0e611622 : 0b11009e00e6116202020070: on>',
|
||||
entity.__str__())
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b11009e00e6116201010070')
|
||||
event.data = bytearray(b'\x0b\x11\x00\x9e\x00\xe6\x11b\x01\x01\x00p')
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(1, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100120118cdea02020070')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x12, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x02, 0x02, 0x00, 0x70])
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = devices[1]
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = rfxtrx_core.RFX_DEVICES['118cdea2']
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
self.assertEqual('<Entity 118cdea2 : 0b1100120118cdea02020070: on>',
|
||||
entity.__str__())
|
||||
|
||||
|
@ -188,75 +179,53 @@ class TestLightRfxtrx(unittest.TestCase):
|
|||
event.data = bytearray(b'\nR\x08^\x07\x01\x00\xb3\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
|
||||
# trying to add a swicth
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdea02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x10, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x01, 0x01, 0x0f, 0x70])
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
|
||||
def test_discover_light_noautoadd(self):
|
||||
"""Test with discover of light when auto add is False."""
|
||||
config = {'automatic_add': False, 'devices': {}}
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertTrue(_setup_component(self.hass, 'light', {
|
||||
'light': {'platform': 'rfxtrx',
|
||||
'automatic_add': False,
|
||||
'devices': {}}}))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100120118cdea02020070')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x12, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x02, 0x02, 0x00, 0x70])
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100120118cdea02010070')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x12, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x02, 0x01, 0x00, 0x70])
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100120118cdea02020070')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x12, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x02, 0x02, 0x00, 0x70])
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
# Trying to add a sensor
|
||||
event = rfxtrx_core.get_rfx_object('0a52085e070100b31b0279')
|
||||
event.data = bytearray(b'\nR\x08^\x07\x01\x00\xb3\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
# Trying to add a switch
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdea02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x10, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x01, 0x01, 0x0f, 0x70])
|
||||
with patch('homeassistant.components.light.' +
|
||||
'rfxtrx.RfxtrxLight.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""The tests for the MQTT lock platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.const import (STATE_LOCKED, STATE_UNLOCKED,
|
||||
ATTR_ASSUMED_STATE)
|
||||
import homeassistant.components.lock as lock
|
||||
|
@ -22,8 +23,9 @@ class TestLockMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic(self):
|
||||
"""Test the controlling state via topic."""
|
||||
self.assertTrue(lock.setup(self.hass, {
|
||||
'lock': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, lock.DOMAIN, {
|
||||
lock.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
|
@ -31,7 +33,7 @@ class TestLockMQTT(unittest.TestCase):
|
|||
'payload_lock': 'LOCK',
|
||||
'payload_unlock': 'UNLOCK'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('lock.test')
|
||||
self.assertEqual(STATE_UNLOCKED, state.state)
|
||||
|
@ -51,8 +53,9 @@ class TestLockMQTT(unittest.TestCase):
|
|||
|
||||
def test_sending_mqtt_commands_and_optimistic(self):
|
||||
"""Test the sending MQTT commands in optimistic mode."""
|
||||
self.assertTrue(lock.setup(self.hass, {
|
||||
'lock': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, lock.DOMAIN, {
|
||||
lock.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'command_topic': 'command-topic',
|
||||
|
@ -60,7 +63,7 @@ class TestLockMQTT(unittest.TestCase):
|
|||
'payload_unlock': 'UNLOCK',
|
||||
'qos': 2
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('lock.test')
|
||||
self.assertEqual(STATE_UNLOCKED, state.state)
|
||||
|
@ -84,8 +87,9 @@ class TestLockMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic_and_json_message(self):
|
||||
"""Test the controlling state via topic and JSON message."""
|
||||
self.assertTrue(lock.setup(self.hass, {
|
||||
'lock': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, lock.DOMAIN, {
|
||||
lock.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
|
@ -94,7 +98,7 @@ class TestLockMQTT(unittest.TestCase):
|
|||
'payload_unlock': 'UNLOCK',
|
||||
'value_template': '{{ value_json.val }}'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('lock.test')
|
||||
self.assertEqual(STATE_UNLOCKED, state.state)
|
||||
|
|
|
@ -22,7 +22,7 @@ class TestDemoMediaPlayer(unittest.TestCase):
|
|||
def test_source_select(self):
|
||||
"""Test the input source service."""
|
||||
|
||||
entity_id = 'media_player.receiver'
|
||||
entity_id = 'media_player.lounge_room'
|
||||
|
||||
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
|
||||
state = self.hass.states.get(entity_id)
|
||||
|
|
|
@ -4,6 +4,9 @@ import unittest
|
|||
from unittest import mock
|
||||
import socket
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.const import (
|
||||
EVENT_CALL_SERVICE, ATTR_DOMAIN, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
|
||||
|
@ -48,9 +51,23 @@ class TestMQTT(unittest.TestCase):
|
|||
"""Test for setup failure if connection to broker is missing."""
|
||||
with mock.patch('homeassistant.components.mqtt.MQTT',
|
||||
side_effect=socket.error()):
|
||||
self.assertFalse(mqtt.setup(self.hass, {mqtt.DOMAIN: {
|
||||
mqtt.CONF_BROKER: 'test-broker',
|
||||
}}))
|
||||
self.hass.config.components = []
|
||||
assert not _setup_component(self.hass, mqtt.DOMAIN, {
|
||||
mqtt.DOMAIN: {
|
||||
mqtt.CONF_BROKER: 'test-broker',
|
||||
}
|
||||
})
|
||||
|
||||
def test_setup_protocol_validation(self):
|
||||
"""Test for setup failure if connection to broker is missing."""
|
||||
with mock.patch('paho.mqtt.client.Client'):
|
||||
self.hass.config.components = []
|
||||
assert _setup_component(self.hass, mqtt.DOMAIN, {
|
||||
mqtt.DOMAIN: {
|
||||
mqtt.CONF_BROKER: 'test-broker',
|
||||
mqtt.CONF_PROTOCOL: 3.1,
|
||||
}
|
||||
})
|
||||
|
||||
def test_publish_calls_service(self):
|
||||
"""Test the publishing of call to services."""
|
||||
|
@ -211,12 +228,12 @@ class TestMQTTCallbacks(unittest.TestCase):
|
|||
# mock_mqtt_component(self.hass)
|
||||
|
||||
with mock.patch('paho.mqtt.client.Client'):
|
||||
mqtt.setup(self.hass, {
|
||||
self.hass.config.components = []
|
||||
assert _setup_component(self.hass, mqtt.DOMAIN, {
|
||||
mqtt.DOMAIN: {
|
||||
mqtt.CONF_BROKER: 'mock-broker',
|
||||
}
|
||||
})
|
||||
self.hass.config.components.append(mqtt.DOMAIN)
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
"""Stop everything that was started."""
|
||||
|
@ -302,3 +319,7 @@ class TestMQTTCallbacks(unittest.TestCase):
|
|||
|
||||
self.assertEqual({'test/topic': 1}, mqtt.MQTT_CLIENT.topics)
|
||||
self.assertEqual({}, mqtt.MQTT_CLIENT.progress)
|
||||
|
||||
def test_invalid_mqtt_topics(self):
|
||||
self.assertRaises(vol.Invalid, mqtt.valid_publish_topic, 'bad+topic')
|
||||
self.assertRaises(vol.Invalid, mqtt.valid_subscribe_topic, 'bad\0one')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""The tests for the MQTT component embedded server."""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
|
@ -27,15 +28,16 @@ class TestMQTT:
|
|||
password = 'super_secret'
|
||||
|
||||
self.hass.config.api = MagicMock(api_password=password)
|
||||
assert mqtt.setup(self.hass, {})
|
||||
assert _setup_component(self.hass, mqtt.DOMAIN, {})
|
||||
assert mock_mqtt.called
|
||||
assert mock_mqtt.mock_calls[0][1][5] == 'homeassistant'
|
||||
assert mock_mqtt.mock_calls[0][1][6] == password
|
||||
|
||||
mock_mqtt.reset_mock()
|
||||
|
||||
self.hass.config.components = ['http']
|
||||
self.hass.config.api = MagicMock(api_password=None)
|
||||
assert mqtt.setup(self.hass, {})
|
||||
assert _setup_component(self.hass, mqtt.DOMAIN, {})
|
||||
assert mock_mqtt.called
|
||||
assert mock_mqtt.mock_calls[0][1][5] is None
|
||||
assert mock_mqtt.mock_calls[0][1][6] is None
|
||||
|
@ -50,6 +52,6 @@ class TestMQTT:
|
|||
mock_gather.side_effect = BrokerException
|
||||
|
||||
self.hass.config.api = MagicMock(api_password=None)
|
||||
assert not mqtt.setup(self.hass, {
|
||||
'mqtt': {'embedded': {}}
|
||||
assert not _setup_component(self.hass, mqtt.DOMAIN, {
|
||||
mqtt.DOMAIN: {mqtt.CONF_EMBEDDED: {}}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""The tests for the MQTT roller shutter platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.const import STATE_OPEN, STATE_CLOSED, STATE_UNKNOWN
|
||||
import homeassistant.components.rollershutter as rollershutter
|
||||
from tests.common import mock_mqtt_component, fire_mqtt_message
|
||||
|
@ -22,8 +23,9 @@ class TestRollershutterMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic(self):
|
||||
"""Test the controlling state via topic."""
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, rollershutter.DOMAIN, {
|
||||
rollershutter.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
|
@ -33,7 +35,7 @@ class TestRollershutterMQTT(unittest.TestCase):
|
|||
'payload_down': 'DOWN',
|
||||
'payload_stop': 'STOP'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_UNKNOWN, state.state)
|
||||
|
@ -58,15 +60,16 @@ class TestRollershutterMQTT(unittest.TestCase):
|
|||
|
||||
def test_send_move_up_command(self):
|
||||
"""Test the sending of move_up."""
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, rollershutter.DOMAIN, {
|
||||
rollershutter.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'qos': 2
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_UNKNOWN, state.state)
|
||||
|
@ -81,15 +84,16 @@ class TestRollershutterMQTT(unittest.TestCase):
|
|||
|
||||
def test_send_move_down_command(self):
|
||||
"""Test the sending of move_down."""
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, rollershutter.DOMAIN, {
|
||||
rollershutter.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'qos': 2
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_UNKNOWN, state.state)
|
||||
|
@ -104,15 +108,16 @@ class TestRollershutterMQTT(unittest.TestCase):
|
|||
|
||||
def test_send_stop_command(self):
|
||||
"""Test the sending of stop."""
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, rollershutter.DOMAIN, {
|
||||
rollershutter.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
'command_topic': 'command-topic',
|
||||
'qos': 2
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('rollershutter.test')
|
||||
self.assertEqual(STATE_UNKNOWN, state.state)
|
||||
|
@ -127,8 +132,9 @@ class TestRollershutterMQTT(unittest.TestCase):
|
|||
|
||||
def test_state_attributes_current_position(self):
|
||||
"""Test the current position."""
|
||||
self.assertTrue(rollershutter.setup(self.hass, {
|
||||
'rollershutter': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, rollershutter.DOMAIN, {
|
||||
rollershutter.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
|
@ -137,7 +143,7 @@ class TestRollershutterMQTT(unittest.TestCase):
|
|||
'payload_down': 'DOWN',
|
||||
'payload_stop': 'STOP'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state_attributes_dict = self.hass.states.get(
|
||||
'rollershutter.test').attributes
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""The tests for the MQTT sensor platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
import homeassistant.components.sensor as sensor
|
||||
from tests.common import mock_mqtt_component, fire_mqtt_message
|
||||
|
||||
|
@ -21,14 +22,15 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_setting_sensor_value_via_mqtt_message(self):
|
||||
"""Test the setting of the value via MQTT."""
|
||||
self.assertTrue(sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, sensor.DOMAIN, {
|
||||
sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test-topic',
|
||||
'unit_of_measurement': 'fav unit'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
fire_mqtt_message(self.hass, 'test-topic', '100')
|
||||
self.hass.pool.block_till_done()
|
||||
|
@ -40,15 +42,16 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_setting_sensor_value_via_mqtt_json_message(self):
|
||||
"""Test the setting of the value via MQTT with JSON playload."""
|
||||
self.assertTrue(sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, sensor.DOMAIN, {
|
||||
sensor.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test-topic',
|
||||
'unit_of_measurement': 'fav unit',
|
||||
'value_template': '{{ value_json.val }}'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
fire_mqtt_message(self.hass, 'test-topic', '{ "val": "100" }')
|
||||
self.hass.pool.block_till_done()
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
"""The tests for the Rfxtrx sensor platform."""
|
||||
import unittest
|
||||
|
||||
|
||||
from homeassistant.components.sensor import rfxtrx
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.components import rfxtrx as rfxtrx_core
|
||||
from homeassistant.const import TEMP_CELCIUS
|
||||
|
||||
|
@ -15,6 +14,7 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant(0)
|
||||
self.hass.config.components = ['rfxtrx']
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
@ -24,35 +24,25 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
|
||||
def test_default_config(self):
|
||||
"""Test with 0 sensor."""
|
||||
config = {'devices': {}}
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertEqual(0, len(devices))
|
||||
self.assertTrue(_setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'rfxtrx',
|
||||
'devices':
|
||||
{}}}))
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
|
||||
def test_one_sensor(self):
|
||||
"""Test with 1 sensor."""
|
||||
config = {'devices':
|
||||
{'sensor_0502': {
|
||||
'name': 'Test',
|
||||
'packetid': '0a52080705020095220269',
|
||||
'data_type': 'Temperature'}}}
|
||||
devices = []
|
||||
self.assertTrue(_setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'rfxtrx',
|
||||
'devices':
|
||||
{'sensor_0502': {
|
||||
'name': 'Test',
|
||||
'packetid': '0a52080705020095220269',
|
||||
'data_type': 'Temperature'}}}}))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
|
||||
self.assertEqual(1, len(devices))
|
||||
entity = devices[0]
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
print(rfxtrx_core.RFX_DEVICES)
|
||||
entity = rfxtrx_core.RFX_DEVICES['sensor_0502']
|
||||
self.assertEqual('Test', entity.name)
|
||||
self.assertEqual(TEMP_CELCIUS, entity.unit_of_measurement)
|
||||
self.assertEqual(14.9, entity.state)
|
||||
|
@ -64,31 +54,25 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
|
||||
def test_several_sensors(self):
|
||||
"""Test with 3 sensors."""
|
||||
config = {'devices':
|
||||
{'sensor_0502': {
|
||||
'name': 'Test',
|
||||
'packetid': '0a52080705020095220269',
|
||||
'data_type': 'Temperature'},
|
||||
'sensor_0601': {
|
||||
'name': 'Bath_Humidity',
|
||||
'packetid': '0a520802060100ff0e0269',
|
||||
'data_type': 'Humidity'},
|
||||
'sensor_0601 2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0a520802060100ff0e0269'}}}
|
||||
devices = []
|
||||
self.assertTrue(_setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'rfxtrx',
|
||||
'devices':
|
||||
{'sensor_0502': {
|
||||
'name': 'Test',
|
||||
'packetid': '0a52080705020095220269',
|
||||
'data_type': 'Temperature'},
|
||||
'sensor_0601': {
|
||||
'name': 'Bath_Humidity',
|
||||
'packetid': '0a520802060100ff0e0269',
|
||||
'data_type': 'Humidity'},
|
||||
'sensor_0601 2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0a520802060100ff0e0269'}}}}))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
|
||||
self.assertEqual(3, len(devices))
|
||||
self.assertEqual(3, len(rfxtrx_core.RFX_DEVICES))
|
||||
device_num = 0
|
||||
for entity in devices:
|
||||
for id in rfxtrx_core.RFX_DEVICES:
|
||||
entity = rfxtrx_core.RFX_DEVICES[id]
|
||||
if entity.name == 'Bath_Humidity':
|
||||
device_num = device_num + 1
|
||||
self.assertEqual('%', entity.unit_of_measurement)
|
||||
|
@ -125,23 +109,17 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
|
||||
def test_discover_sensor(self):
|
||||
"""Test with discovery of sensor."""
|
||||
config = {'devices': {}}
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertTrue(_setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices': {}}}))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0a520801070100b81b0279')
|
||||
event.data = bytearray(b'\nR\x08\x01\x07\x01\x00\xb8\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
entity = devices[0]
|
||||
entity = rfxtrx_core.RFX_DEVICES['sensor_0701']
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(1, len(devices))
|
||||
self.assertEqual({'Humidity status': 'normal',
|
||||
'Temperature': 18.4,
|
||||
'Rssi numeric': 7, 'Humidity': 27,
|
||||
|
@ -153,14 +131,12 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(1, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0a52080405020095240279')
|
||||
event.data = bytearray(b'\nR\x08\x04\x05\x02\x00\x95$\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = devices[1]
|
||||
entity = rfxtrx_core.RFX_DEVICES['sensor_0502']
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
self.assertEqual({'Humidity status': 'normal',
|
||||
'Temperature': 14.9,
|
||||
'Rssi numeric': 7, 'Humidity': 36,
|
||||
|
@ -173,9 +149,8 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
event = rfxtrx_core.get_rfx_object('0a52085e070100b31b0279')
|
||||
event.data = bytearray(b'\nR\x08^\x07\x01\x00\xb3\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = devices[0]
|
||||
entity = rfxtrx_core.RFX_DEVICES['sensor_0701']
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
self.assertEqual({'Humidity status': 'normal',
|
||||
'Temperature': 17.9,
|
||||
'Rssi numeric': 7, 'Humidity': 27,
|
||||
|
@ -189,71 +164,56 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
event = rfxtrx_core.get_rfx_object('0b1100cd0213c7f210010f70')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
|
||||
def test_discover_sensor_noautoadd(self):
|
||||
"""Test with discover of sensor when auto add is False."""
|
||||
config = {'automatic_add': False, 'devices': {}}
|
||||
devices = []
|
||||
self.assertTrue(_setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'rfxtrx',
|
||||
'automatic_add': False,
|
||||
'devices': {}}}))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
event = rfxtrx_core.get_rfx_object('0a520801070100b81b0279')
|
||||
event.data = bytearray(b'\nR\x08\x01\x07\x01\x00\xb8\x1b\x02y')
|
||||
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0a52080405020095240279')
|
||||
event.data = bytearray(b'\nR\x08\x04\x05\x02\x00\x95$\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0a52085e070100b31b0279')
|
||||
event.data = bytearray(b'\nR\x08^\x07\x01\x00\xb3\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
def test_update_of_sensors(self):
|
||||
"""Test with 3 sensors."""
|
||||
config = {'devices':
|
||||
{'sensor_0502': {
|
||||
'name': 'Test',
|
||||
'packetid': '0a52080705020095220269',
|
||||
'data_type': 'Temperature'},
|
||||
'sensor_0601': {
|
||||
'name': 'Bath_Humidity',
|
||||
'packetid': '0a520802060100ff0e0269',
|
||||
'data_type': 'Humidity'},
|
||||
'sensor_0601 2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0a520802060100ff0e0269'}}}
|
||||
devices = []
|
||||
self.assertTrue(_setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'rfxtrx',
|
||||
'devices':
|
||||
{'sensor_0502': {
|
||||
'name': 'Test',
|
||||
'packetid': '0a52080705020095220269',
|
||||
'data_type': 'Temperature'},
|
||||
'sensor_0601': {
|
||||
'name': 'Bath_Humidity',
|
||||
'packetid': '0a520802060100ff0e0269',
|
||||
'data_type': 'Humidity'},
|
||||
'sensor_0601 2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0a520802060100ff0e0269'}}}}))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
|
||||
self.assertEqual(3, len(devices))
|
||||
self.assertEqual(3, len(rfxtrx_core.RFX_DEVICES))
|
||||
|
||||
device_num = 0
|
||||
for entity in devices:
|
||||
for id in rfxtrx_core.RFX_DEVICES:
|
||||
entity = rfxtrx_core.RFX_DEVICES[id]
|
||||
if entity.name == 'Bath_Humidity':
|
||||
device_num = device_num + 1
|
||||
self.assertEqual('%', entity.unit_of_measurement)
|
||||
|
@ -291,18 +251,17 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
event = rfxtrx_core.get_rfx_object('0a520802060101ff0f0269')
|
||||
event.data = bytearray(b'\nR\x08\x01\x07\x01\x00\xb8\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = devices[0]
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
event = rfxtrx_core.get_rfx_object('0a52080705020085220269')
|
||||
event.data = bytearray(b'\nR\x08\x04\x05\x02\x00\x95$\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
|
||||
self.assertEqual(3, len(devices))
|
||||
self.assertEqual(3, len(rfxtrx_core.RFX_DEVICES))
|
||||
|
||||
device_num = 0
|
||||
for entity in devices:
|
||||
for id in rfxtrx_core.RFX_DEVICES:
|
||||
entity = rfxtrx_core.RFX_DEVICES[id]
|
||||
if entity.name == 'Bath_Humidity':
|
||||
device_num = device_num + 1
|
||||
self.assertEqual('%', entity.unit_of_measurement)
|
||||
|
@ -337,5 +296,4 @@ class TestSensorRfxtrx(unittest.TestCase):
|
|||
|
||||
self.assertEqual(3, device_num)
|
||||
|
||||
self.assertEqual(3, len(devices))
|
||||
self.assertEqual(3, len(rfxtrx_core.RFX_DEVICES))
|
||||
|
|
|
@ -4,9 +4,8 @@ from unittest.mock import patch
|
|||
|
||||
import pytest
|
||||
|
||||
import homeassistant.components.sensor as sensor
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
|
||||
|
@ -32,12 +31,9 @@ class TestSensorYr:
|
|||
return_value=betamax_session):
|
||||
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'yr',
|
||||
'elevation': 0,
|
||||
}
|
||||
})
|
||||
assert _setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'yr',
|
||||
'elevation': 0}})
|
||||
|
||||
state = self.hass.states.get('sensor.yr_symbol')
|
||||
|
||||
|
@ -53,19 +49,15 @@ class TestSensorYr:
|
|||
return_value=betamax_session):
|
||||
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'yr',
|
||||
'elevation': 0,
|
||||
'monitored_conditions': {
|
||||
'pressure',
|
||||
'windDirection',
|
||||
'humidity',
|
||||
'fog',
|
||||
'windSpeed'
|
||||
}
|
||||
}
|
||||
})
|
||||
assert _setup_component(self.hass, 'sensor', {
|
||||
'sensor': {'platform': 'yr',
|
||||
'elevation': 0,
|
||||
'monitored_conditions': [
|
||||
'pressure',
|
||||
'windDirection',
|
||||
'humidity',
|
||||
'fog',
|
||||
'windSpeed']}})
|
||||
|
||||
state = self.hass.states.get('sensor.yr_pressure')
|
||||
assert 'hPa' == state.attributes.get('unit_of_measurement')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""The tests for the MQTT switch platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ASSUMED_STATE
|
||||
import homeassistant.components.switch as switch
|
||||
from tests.common import (
|
||||
|
@ -21,8 +22,9 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic(self):
|
||||
"""Test the controlling state via topic."""
|
||||
self.assertTrue(switch.setup(self.hass, {
|
||||
'switch': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, switch.DOMAIN, {
|
||||
switch.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
|
@ -30,7 +32,7 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
'payload_on': 1,
|
||||
'payload_off': 0
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('switch.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -50,8 +52,9 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_sending_mqtt_commands_and_optimistic(self):
|
||||
"""Test the sending MQTT commands in optimistic mode."""
|
||||
self.assertTrue(switch.setup(self.hass, {
|
||||
'switch': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, switch.DOMAIN, {
|
||||
switch.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'command_topic': 'command-topic',
|
||||
|
@ -59,7 +62,7 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
'payload_off': 'beer off',
|
||||
'qos': '2'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('switch.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
@ -83,8 +86,9 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
|
||||
def test_controlling_state_via_topic_and_json_message(self):
|
||||
"""Test the controlling state via topic and JSON message."""
|
||||
self.assertTrue(switch.setup(self.hass, {
|
||||
'switch': {
|
||||
self.hass.config.components = ['mqtt']
|
||||
assert _setup_component(self.hass, switch.DOMAIN, {
|
||||
switch.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'state-topic',
|
||||
|
@ -93,7 +97,7 @@ class TestSensorMQTT(unittest.TestCase):
|
|||
'payload_off': 'beer off',
|
||||
'value_template': '{{ value_json.val }}'
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
state = self.hass.states.get('switch.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
"""The tests for the Rfxtrx switch platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.components import rfxtrx as rfxtrx_core
|
||||
from homeassistant.components.switch import rfxtrx
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
|
@ -14,6 +13,7 @@ class TestSwitchRfxtrx(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant(0)
|
||||
self.hass.config.components = ['rfxtrx']
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
|
@ -23,40 +23,98 @@ class TestSwitchRfxtrx(unittest.TestCase):
|
|||
rfxtrx_core.RFXOBJECT.close_connection()
|
||||
self.hass.stop()
|
||||
|
||||
def test_valid_config(self):
|
||||
"""Test configuration."""
|
||||
self.assertTrue(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices':
|
||||
{'213c7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51',
|
||||
rfxtrx_core.ATTR_FIREEVENT: True}
|
||||
}}}))
|
||||
|
||||
def test_invalid_config1(self):
|
||||
self.assertFalse(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices':
|
||||
{'2FF7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51',
|
||||
'signal_repetitions': 3}
|
||||
}}}))
|
||||
|
||||
def test_invalid_config2(self):
|
||||
"""Test configuration."""
|
||||
self.assertFalse(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'invalid_key': 'afda',
|
||||
'devices':
|
||||
{'213c7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51',
|
||||
rfxtrx_core.ATTR_FIREEVENT: True}
|
||||
}}}))
|
||||
|
||||
def test_invalid_config3(self):
|
||||
self.assertFalse(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices':
|
||||
{'213c7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': 'AA1100cd0213c7f210010f51',
|
||||
rfxtrx_core.ATTR_FIREEVENT: True}
|
||||
}}}))
|
||||
|
||||
def test_invalid_config4(self):
|
||||
self.assertFalse(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices':
|
||||
{'AA3c7f216': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51',
|
||||
rfxtrx_core.ATTR_FIREEVENT: True}
|
||||
}}}))
|
||||
|
||||
def test_invalid_config5(self):
|
||||
"""Test configuration."""
|
||||
self.assertFalse(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices':
|
||||
{'213c7f216': {
|
||||
'name': 'Test',
|
||||
rfxtrx_core.ATTR_FIREEVENT: True}
|
||||
}}}))
|
||||
|
||||
def test_default_config(self):
|
||||
"""Test with 0 switches."""
|
||||
config = {'devices': {}}
|
||||
devices = []
|
||||
self.assertTrue(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'devices':
|
||||
{}}}))
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
def test_one_sensor(self):
|
||||
def test_one_switch(self):
|
||||
"""Test with 1 switch."""
|
||||
config = {'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51'}}}
|
||||
self.assertTrue(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f210010f51'}}}}))
|
||||
|
||||
import RFXtrx as rfxtrxmod
|
||||
rfxtrx_core.RFXOBJECT =\
|
||||
rfxtrxmod.Core("", transport_protocol=rfxtrxmod.DummyTransport)
|
||||
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
|
||||
self.assertEqual(1, len(devices))
|
||||
entity = devices[0]
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
entity = rfxtrx_core.RFX_DEVICES['123efab1']
|
||||
self.assertEqual('Test', entity.name)
|
||||
self.assertEqual('off', entity.state)
|
||||
self.assertTrue(entity.assumed_state)
|
||||
|
@ -65,42 +123,31 @@ class TestSwitchRfxtrx(unittest.TestCase):
|
|||
self.assertFalse(entity.should_poll)
|
||||
|
||||
self.assertFalse(entity.is_on)
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
entity.turn_on()
|
||||
entity.turn_on()
|
||||
self.assertTrue(entity.is_on)
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
entity.turn_off()
|
||||
entity.turn_off()
|
||||
self.assertFalse(entity.is_on)
|
||||
|
||||
def test_several_switchs(self):
|
||||
def test_several_switches(self):
|
||||
"""Test with 3 switches."""
|
||||
config = {'signal_repetitions': 3,
|
||||
'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f230010f71'},
|
||||
'118cdea2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0b1100100118cdea02010f70'},
|
||||
'213c7f216': {
|
||||
'name': 'Living',
|
||||
'packetid': '2b1121cd1213c7f211111f71'}}}
|
||||
devices = []
|
||||
self.assertTrue(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'signal_repetitions': 3,
|
||||
'devices':
|
||||
{'123efab1': {
|
||||
'name': 'Test',
|
||||
'packetid': '0b1100cd0213c7f230010f71'},
|
||||
'118cdea2': {
|
||||
'name': 'Bath',
|
||||
'packetid': '0b1100100118cdea02010f70'},
|
||||
'213c7f216': {
|
||||
'name': 'Living',
|
||||
'packetid': '0b1100100118cdea02010f70'}}}}))
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
|
||||
self.assertEqual(3, len(devices))
|
||||
self.assertEqual(3, len(rfxtrx_core.RFX_DEVICES))
|
||||
device_num = 0
|
||||
for entity in devices:
|
||||
for id in rfxtrx_core.RFX_DEVICES:
|
||||
entity = rfxtrx_core.RFX_DEVICES[id]
|
||||
self.assertEqual(entity.signal_repetitions, 3)
|
||||
if entity.name == 'Living':
|
||||
device_num = device_num + 1
|
||||
|
@ -119,46 +166,31 @@ class TestSwitchRfxtrx(unittest.TestCase):
|
|||
|
||||
def test_discover_switch(self):
|
||||
"""Test with discovery of switches."""
|
||||
config = {'automatic_add': True, 'devices': {}}
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertTrue(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': True,
|
||||
'devices': {}}}))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdea02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x10, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x01, 0x01, 0x0f, 0x70])
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = devices[0]
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = rfxtrx_core.RFX_DEVICES['118cdea2']
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(1, len(devices))
|
||||
self.assertEqual('<Entity 118cdea2 : 0b1100100118cdea01010f70: on>',
|
||||
entity.__str__())
|
||||
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(1, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(1, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdeb02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x12, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x02, 0x00, 0x00, 0x70])
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = devices[1]
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
entity = rfxtrx_core.RFX_DEVICES['118cdeb2']
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
self.assertEqual('<Entity 118cdeb2 : 0b1100120118cdea02000070: on>',
|
||||
entity.__str__())
|
||||
|
||||
|
@ -167,72 +199,47 @@ class TestSwitchRfxtrx(unittest.TestCase):
|
|||
event.data = bytearray(b'\nR\x08^\x07\x01\x00\xb3\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
|
||||
# Trying to add a light
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdea02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x11, 0x10, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x01, 0x02, 0x0f, 0x70])
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(2, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(2, len(devices))
|
||||
|
||||
def test_discover_switch_noautoadd(self):
|
||||
"""Test with discovery of switch when auto add is False."""
|
||||
config = {'automatic_add': False, 'devices': {}}
|
||||
devices = []
|
||||
|
||||
def add_dev_callback(devs):
|
||||
"""Add a callback to add devices."""
|
||||
for dev in devs:
|
||||
devices.append(dev)
|
||||
|
||||
rfxtrx.setup_platform(self.hass, config, add_dev_callback)
|
||||
self.assertTrue(_setup_component(self.hass, 'switch', {
|
||||
'switch': {'platform': 'rfxtrx',
|
||||
'automatic_add': False,
|
||||
'devices': {}}}))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdea02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x10, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x01, 0x01, 0x0f, 0x70])
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdeb02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x00, 0x12, 0x01, 0x18,
|
||||
0xcd, 0xea, 0x02, 0x00, 0x00, 0x70])
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
# Trying to add a sensor
|
||||
event = rfxtrx_core.get_rfx_object('0a52085e070100b31b0279')
|
||||
event.data = bytearray(b'\nR\x08^\x07\x01\x00\xb3\x1b\x02y')
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
||||
# Trying to add a light
|
||||
event = rfxtrx_core.get_rfx_object('0b1100100118cdea02010f70')
|
||||
event.data = bytearray([0x0b, 0x11, 0x11, 0x10, 0x01,
|
||||
0x18, 0xcd, 0xea, 0x01, 0x02, 0x0f, 0x70])
|
||||
with patch('homeassistant.components.switch.' +
|
||||
'rfxtrx.RfxtrxSwitch.update_ha_state',
|
||||
return_value=None):
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
rfxtrx_core.RECEIVED_EVT_SUBSCRIBERS[0](event)
|
||||
self.assertEqual(0, len(rfxtrx_core.RFX_DEVICES))
|
||||
self.assertEqual(0, len(devices))
|
||||
|
|
|
@ -226,6 +226,7 @@ class TestComponentsGroup(unittest.TestCase):
|
|||
'view': True,
|
||||
},
|
||||
'test_group': 'hello.world,sensor.happy',
|
||||
'empty_group': {'name': 'Empty Group', 'entities': None},
|
||||
}
|
||||
})
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue