mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
Remove deprecated Wink integration (#57634)
This commit is contained in:
parent
b54fc0229d
commit
45f3eb6991
@ -1191,7 +1191,6 @@ omit =
|
|||||||
homeassistant/components/webostv/*
|
homeassistant/components/webostv/*
|
||||||
homeassistant/components/whois/sensor.py
|
homeassistant/components/whois/sensor.py
|
||||||
homeassistant/components/wiffi/*
|
homeassistant/components/wiffi/*
|
||||||
homeassistant/components/wink/*
|
|
||||||
homeassistant/components/wirelesstag/*
|
homeassistant/components/wirelesstag/*
|
||||||
homeassistant/components/wolflink/__init__.py
|
homeassistant/components/wolflink/__init__.py
|
||||||
homeassistant/components/wolflink/sensor.py
|
homeassistant/components/wolflink/sensor.py
|
||||||
|
@ -38,7 +38,6 @@ SERVICE_SAMSUNG_PRINTER = "samsung_printer"
|
|||||||
SERVICE_TELLDUSLIVE = "tellstick"
|
SERVICE_TELLDUSLIVE = "tellstick"
|
||||||
SERVICE_YEELIGHT = "yeelight"
|
SERVICE_YEELIGHT = "yeelight"
|
||||||
SERVICE_WEMO = "belkin_wemo"
|
SERVICE_WEMO = "belkin_wemo"
|
||||||
SERVICE_WINK = "wink"
|
|
||||||
SERVICE_XIAOMI_GW = "xiaomi_gw"
|
SERVICE_XIAOMI_GW = "xiaomi_gw"
|
||||||
|
|
||||||
# These have custom protocols
|
# These have custom protocols
|
||||||
@ -94,7 +93,6 @@ MIGRATED_SERVICE_HANDLERS = [
|
|||||||
"sonos",
|
"sonos",
|
||||||
"songpal",
|
"songpal",
|
||||||
SERVICE_WEMO,
|
SERVICE_WEMO,
|
||||||
SERVICE_WINK,
|
|
||||||
SERVICE_XIAOMI_GW,
|
SERVICE_XIAOMI_GW,
|
||||||
"volumio",
|
"volumio",
|
||||||
SERVICE_YEELIGHT,
|
SERVICE_YEELIGHT,
|
||||||
|
@ -1,971 +0,0 @@
|
|||||||
"""Support for Wink hubs."""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from aiohttp.web import Response
|
|
||||||
from pubnubsubhandler import PubNubSubscriptionHandler
|
|
||||||
import pywink
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
|
||||||
from homeassistant.const import (
|
|
||||||
ATTR_BATTERY_LEVEL,
|
|
||||||
ATTR_NAME,
|
|
||||||
CONF_CLIENT_ID,
|
|
||||||
CONF_CLIENT_SECRET,
|
|
||||||
CONF_EMAIL,
|
|
||||||
CONF_PASSWORD,
|
|
||||||
EVENT_HOMEASSISTANT_START,
|
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
|
||||||
STATE_OFF,
|
|
||||||
STATE_ON,
|
|
||||||
__version__,
|
|
||||||
)
|
|
||||||
from homeassistant.core import callback
|
|
||||||
from homeassistant.helpers import discovery
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.config_validation import make_entity_service_schema
|
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
|
||||||
from homeassistant.helpers.event import track_time_interval
|
|
||||||
from homeassistant.helpers.network import get_url
|
|
||||||
from homeassistant.util.json import load_json, save_json
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
DOMAIN = "wink"
|
|
||||||
|
|
||||||
SUBSCRIPTION_HANDLER = None
|
|
||||||
|
|
||||||
CONF_USER_AGENT = "user_agent"
|
|
||||||
CONF_OAUTH = "oauth"
|
|
||||||
CONF_LOCAL_CONTROL = "local_control"
|
|
||||||
CONF_MISSING_OAUTH_MSG = "Missing oauth2 credentials."
|
|
||||||
|
|
||||||
ATTR_ACCESS_TOKEN = "access_token"
|
|
||||||
ATTR_REFRESH_TOKEN = "refresh_token"
|
|
||||||
ATTR_PAIRING_MODE = "pairing_mode"
|
|
||||||
ATTR_KIDDE_RADIO_CODE = "kidde_radio_code"
|
|
||||||
ATTR_HUB_NAME = "hub_name"
|
|
||||||
|
|
||||||
WINK_AUTH_CALLBACK_PATH = "/auth/wink/callback"
|
|
||||||
WINK_AUTH_START = "/auth/wink"
|
|
||||||
WINK_CONFIG_FILE = ".wink.conf"
|
|
||||||
USER_AGENT = f"Manufacturer/Home-Assistant{__version__} python/3 Wink/3"
|
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
CONF_CLIENT_ID: "CLIENT_ID_HERE",
|
|
||||||
CONF_CLIENT_SECRET: "CLIENT_SECRET_HERE",
|
|
||||||
}
|
|
||||||
|
|
||||||
SERVICE_ADD_NEW_DEVICES = "pull_newly_added_devices_from_wink"
|
|
||||||
SERVICE_REFRESH_STATES = "refresh_state_from_wink"
|
|
||||||
SERVICE_RENAME_DEVICE = "rename_wink_device"
|
|
||||||
SERVICE_DELETE_DEVICE = "delete_wink_device"
|
|
||||||
SERVICE_SET_PAIRING_MODE = "pair_new_device"
|
|
||||||
SERVICE_SET_CHIME_VOLUME = "set_chime_volume"
|
|
||||||
SERVICE_SET_SIREN_VOLUME = "set_siren_volume"
|
|
||||||
SERVICE_ENABLE_CHIME = "enable_chime"
|
|
||||||
SERVICE_SET_SIREN_TONE = "set_siren_tone"
|
|
||||||
SERVICE_SET_AUTO_SHUTOFF = "siren_set_auto_shutoff"
|
|
||||||
SERVICE_SIREN_STROBE_ENABLED = "set_siren_strobe_enabled"
|
|
||||||
SERVICE_CHIME_STROBE_ENABLED = "set_chime_strobe_enabled"
|
|
||||||
SERVICE_ENABLE_SIREN = "enable_siren"
|
|
||||||
SERVICE_SET_DIAL_CONFIG = "set_nimbus_dial_configuration"
|
|
||||||
SERVICE_SET_DIAL_STATE = "set_nimbus_dial_state"
|
|
||||||
|
|
||||||
ATTR_VOLUME = "volume"
|
|
||||||
ATTR_TONE = "tone"
|
|
||||||
ATTR_ENABLED = "enabled"
|
|
||||||
ATTR_AUTO_SHUTOFF = "auto_shutoff"
|
|
||||||
ATTR_MIN_VALUE = "min_value"
|
|
||||||
ATTR_MAX_VALUE = "max_value"
|
|
||||||
ATTR_ROTATION = "rotation"
|
|
||||||
ATTR_SCALE = "scale"
|
|
||||||
ATTR_TICKS = "ticks"
|
|
||||||
ATTR_MIN_POSITION = "min_position"
|
|
||||||
ATTR_MAX_POSITION = "max_position"
|
|
||||||
ATTR_VALUE = "value"
|
|
||||||
ATTR_LABELS = "labels"
|
|
||||||
|
|
||||||
SCALES = ["linear", "log"]
|
|
||||||
ROTATIONS = ["cw", "ccw"]
|
|
||||||
|
|
||||||
VOLUMES = ["low", "medium", "high"]
|
|
||||||
TONES = [
|
|
||||||
"doorbell",
|
|
||||||
"fur_elise",
|
|
||||||
"doorbell_extended",
|
|
||||||
"alert",
|
|
||||||
"william_tell",
|
|
||||||
"rondo_alla_turca",
|
|
||||||
"police_siren",
|
|
||||||
"evacuation",
|
|
||||||
"beep_beep",
|
|
||||||
"beep",
|
|
||||||
]
|
|
||||||
CHIME_TONES = TONES + ["inactive"]
|
|
||||||
AUTO_SHUTOFF_TIMES = [None, -1, 30, 60, 120]
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
|
||||||
vol.All(
|
|
||||||
cv.deprecated(DOMAIN),
|
|
||||||
{
|
|
||||||
DOMAIN: vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Inclusive(
|
|
||||||
CONF_EMAIL, CONF_OAUTH, msg=CONF_MISSING_OAUTH_MSG
|
|
||||||
): cv.string,
|
|
||||||
vol.Inclusive(
|
|
||||||
CONF_PASSWORD, CONF_OAUTH, msg=CONF_MISSING_OAUTH_MSG
|
|
||||||
): cv.string,
|
|
||||||
vol.Inclusive(
|
|
||||||
CONF_CLIENT_ID, CONF_OAUTH, msg=CONF_MISSING_OAUTH_MSG
|
|
||||||
): cv.string,
|
|
||||||
vol.Inclusive(
|
|
||||||
CONF_CLIENT_SECRET, CONF_OAUTH, msg=CONF_MISSING_OAUTH_MSG
|
|
||||||
): cv.string,
|
|
||||||
vol.Optional(CONF_LOCAL_CONTROL, default=False): cv.boolean,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
extra=vol.ALLOW_EXTRA,
|
|
||||||
)
|
|
||||||
|
|
||||||
RENAME_DEVICE_SCHEMA = make_entity_service_schema(
|
|
||||||
{vol.Required(ATTR_NAME): cv.string}, extra=vol.ALLOW_EXTRA
|
|
||||||
)
|
|
||||||
|
|
||||||
DELETE_DEVICE_SCHEMA = make_entity_service_schema({}, extra=vol.ALLOW_EXTRA)
|
|
||||||
|
|
||||||
SET_PAIRING_MODE_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Required(ATTR_HUB_NAME): cv.string,
|
|
||||||
vol.Required(ATTR_PAIRING_MODE): cv.string,
|
|
||||||
vol.Optional(ATTR_KIDDE_RADIO_CODE): cv.string,
|
|
||||||
},
|
|
||||||
extra=vol.ALLOW_EXTRA,
|
|
||||||
)
|
|
||||||
|
|
||||||
SET_VOLUME_SCHEMA = make_entity_service_schema(
|
|
||||||
{vol.Required(ATTR_VOLUME): vol.In(VOLUMES)}
|
|
||||||
)
|
|
||||||
|
|
||||||
SET_SIREN_TONE_SCHEMA = make_entity_service_schema(
|
|
||||||
{vol.Required(ATTR_TONE): vol.In(TONES)}
|
|
||||||
)
|
|
||||||
|
|
||||||
SET_CHIME_MODE_SCHEMA = make_entity_service_schema(
|
|
||||||
{vol.Required(ATTR_TONE): vol.In(CHIME_TONES)}
|
|
||||||
)
|
|
||||||
|
|
||||||
SET_AUTO_SHUTOFF_SCHEMA = make_entity_service_schema(
|
|
||||||
{vol.Required(ATTR_AUTO_SHUTOFF): vol.In(AUTO_SHUTOFF_TIMES)}
|
|
||||||
)
|
|
||||||
|
|
||||||
SET_STROBE_ENABLED_SCHEMA = make_entity_service_schema(
|
|
||||||
{vol.Required(ATTR_ENABLED): cv.boolean}
|
|
||||||
)
|
|
||||||
|
|
||||||
ENABLED_SIREN_SCHEMA = make_entity_service_schema(
|
|
||||||
{vol.Required(ATTR_ENABLED): cv.boolean}
|
|
||||||
)
|
|
||||||
|
|
||||||
DIAL_CONFIG_SCHEMA = make_entity_service_schema(
|
|
||||||
{
|
|
||||||
vol.Optional(ATTR_MIN_VALUE): vol.Coerce(int),
|
|
||||||
vol.Optional(ATTR_MAX_VALUE): vol.Coerce(int),
|
|
||||||
vol.Optional(ATTR_MIN_POSITION): cv.positive_int,
|
|
||||||
vol.Optional(ATTR_MAX_POSITION): cv.positive_int,
|
|
||||||
vol.Optional(ATTR_ROTATION): vol.In(ROTATIONS),
|
|
||||||
vol.Optional(ATTR_SCALE): vol.In(SCALES),
|
|
||||||
vol.Optional(ATTR_TICKS): cv.positive_int,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
DIAL_STATE_SCHEMA = make_entity_service_schema(
|
|
||||||
{
|
|
||||||
vol.Required(ATTR_VALUE): vol.Coerce(int),
|
|
||||||
vol.Optional(ATTR_LABELS): cv.ensure_list(cv.string),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
WINK_COMPONENTS = [
|
|
||||||
"binary_sensor",
|
|
||||||
"sensor",
|
|
||||||
"light",
|
|
||||||
"switch",
|
|
||||||
"lock",
|
|
||||||
"cover",
|
|
||||||
"climate",
|
|
||||||
"fan",
|
|
||||||
"alarm_control_panel",
|
|
||||||
"scene",
|
|
||||||
"water_heater",
|
|
||||||
]
|
|
||||||
|
|
||||||
WINK_HUBS: list[Any] = []
|
|
||||||
|
|
||||||
|
|
||||||
def _request_app_setup(hass, config):
|
|
||||||
"""Assist user with configuring the Wink dev application."""
|
|
||||||
hass.data[DOMAIN]["configurator"] = True
|
|
||||||
configurator = hass.components.configurator
|
|
||||||
|
|
||||||
def wink_configuration_callback(callback_data):
|
|
||||||
"""Handle configuration updates."""
|
|
||||||
_config_path = hass.config.path(WINK_CONFIG_FILE)
|
|
||||||
if not os.path.isfile(_config_path):
|
|
||||||
setup(hass, config)
|
|
||||||
return
|
|
||||||
|
|
||||||
client_id = callback_data.get(CONF_CLIENT_ID).strip()
|
|
||||||
client_secret = callback_data.get(CONF_CLIENT_SECRET).strip()
|
|
||||||
if None not in (client_id, client_secret):
|
|
||||||
save_json(
|
|
||||||
_config_path,
|
|
||||||
{CONF_CLIENT_ID: client_id, CONF_CLIENT_SECRET: client_secret},
|
|
||||||
)
|
|
||||||
setup(hass, config)
|
|
||||||
return
|
|
||||||
error_msg = "Your input was invalid. Please try again."
|
|
||||||
_configurator = hass.data[DOMAIN]["configuring"][DOMAIN]
|
|
||||||
configurator.notify_errors(_configurator, error_msg)
|
|
||||||
|
|
||||||
start_url = f"{get_url(hass)}{WINK_AUTH_CALLBACK_PATH}"
|
|
||||||
|
|
||||||
description = f"""Please create a Wink developer app at
|
|
||||||
https://developer.wink.com.
|
|
||||||
Add a Redirect URI of {start_url}.
|
|
||||||
They will provide you a Client ID and secret
|
|
||||||
after reviewing your request.
|
|
||||||
(This can take several days).
|
|
||||||
"""
|
|
||||||
|
|
||||||
hass.data[DOMAIN]["configuring"][DOMAIN] = configurator.request_config(
|
|
||||||
DOMAIN,
|
|
||||||
wink_configuration_callback,
|
|
||||||
description=description,
|
|
||||||
submit_caption="submit",
|
|
||||||
description_image="/static/images/config_wink.png",
|
|
||||||
fields=[
|
|
||||||
{"id": CONF_CLIENT_ID, "name": "Client ID", "type": "string"},
|
|
||||||
{"id": CONF_CLIENT_SECRET, "name": "Client secret", "type": "string"},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _request_oauth_completion(hass, config):
|
|
||||||
"""Request user complete Wink OAuth2 flow."""
|
|
||||||
hass.data[DOMAIN]["configurator"] = True
|
|
||||||
configurator = hass.components.configurator
|
|
||||||
if DOMAIN in hass.data[DOMAIN]["configuring"]:
|
|
||||||
configurator.notify_errors(
|
|
||||||
hass.data[DOMAIN]["configuring"][DOMAIN],
|
|
||||||
"Failed to register, please try again.",
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
def wink_configuration_callback(callback_data):
|
|
||||||
"""Call setup again."""
|
|
||||||
setup(hass, config)
|
|
||||||
|
|
||||||
start_url = f"{get_url(hass)}{WINK_AUTH_START}"
|
|
||||||
|
|
||||||
description = f"Please authorize Wink by visiting {start_url}"
|
|
||||||
|
|
||||||
hass.data[DOMAIN]["configuring"][DOMAIN] = configurator.request_config(
|
|
||||||
DOMAIN, wink_configuration_callback, description=description
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config): # noqa: C901
|
|
||||||
"""Set up the Wink component."""
|
|
||||||
_LOGGER.warning(
|
|
||||||
"The Wink integration has been deprecated and is pending removal in "
|
|
||||||
"Home Assistant Core 2021.11"
|
|
||||||
)
|
|
||||||
|
|
||||||
if hass.data.get(DOMAIN) is None:
|
|
||||||
hass.data[DOMAIN] = {
|
|
||||||
"unique_ids": [],
|
|
||||||
"entities": {},
|
|
||||||
"oauth": {},
|
|
||||||
"configuring": {},
|
|
||||||
"pubnub": None,
|
|
||||||
"configurator": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.get(DOMAIN) is not None:
|
|
||||||
client_id = config[DOMAIN].get(CONF_CLIENT_ID)
|
|
||||||
client_secret = config[DOMAIN].get(CONF_CLIENT_SECRET)
|
|
||||||
email = config[DOMAIN].get(CONF_EMAIL)
|
|
||||||
password = config[DOMAIN].get(CONF_PASSWORD)
|
|
||||||
local_control = config[DOMAIN].get(CONF_LOCAL_CONTROL)
|
|
||||||
else:
|
|
||||||
client_id = None
|
|
||||||
client_secret = None
|
|
||||||
email = None
|
|
||||||
password = None
|
|
||||||
local_control = None
|
|
||||||
hass.data[DOMAIN]["configurator"] = True
|
|
||||||
if None not in [client_id, client_secret]:
|
|
||||||
_LOGGER.info("Using legacy OAuth authentication")
|
|
||||||
if not local_control:
|
|
||||||
pywink.disable_local_control()
|
|
||||||
hass.data[DOMAIN]["oauth"][CONF_CLIENT_ID] = client_id
|
|
||||||
hass.data[DOMAIN]["oauth"][CONF_CLIENT_SECRET] = client_secret
|
|
||||||
hass.data[DOMAIN]["oauth"]["email"] = email
|
|
||||||
hass.data[DOMAIN]["oauth"]["password"] = password
|
|
||||||
pywink.legacy_set_wink_credentials(email, password, client_id, client_secret)
|
|
||||||
else:
|
|
||||||
_LOGGER.info("Using OAuth authentication")
|
|
||||||
if not local_control:
|
|
||||||
pywink.disable_local_control()
|
|
||||||
config_path = hass.config.path(WINK_CONFIG_FILE)
|
|
||||||
if os.path.isfile(config_path):
|
|
||||||
config_file = load_json(config_path)
|
|
||||||
if config_file == DEFAULT_CONFIG:
|
|
||||||
_request_app_setup(hass, config)
|
|
||||||
return True
|
|
||||||
# else move on because the user modified the file
|
|
||||||
else:
|
|
||||||
save_json(config_path, DEFAULT_CONFIG)
|
|
||||||
_request_app_setup(hass, config)
|
|
||||||
return True
|
|
||||||
|
|
||||||
if DOMAIN in hass.data[DOMAIN]["configuring"]:
|
|
||||||
_configurator = hass.data[DOMAIN]["configuring"]
|
|
||||||
hass.components.configurator.request_done(_configurator.pop(DOMAIN))
|
|
||||||
|
|
||||||
# Using oauth
|
|
||||||
access_token = config_file.get(ATTR_ACCESS_TOKEN)
|
|
||||||
refresh_token = config_file.get(ATTR_REFRESH_TOKEN)
|
|
||||||
|
|
||||||
# This will be called after authorizing Home-Assistant
|
|
||||||
if None not in (access_token, refresh_token):
|
|
||||||
pywink.set_wink_credentials(
|
|
||||||
config_file.get(CONF_CLIENT_ID),
|
|
||||||
config_file.get(CONF_CLIENT_SECRET),
|
|
||||||
access_token=access_token,
|
|
||||||
refresh_token=refresh_token,
|
|
||||||
)
|
|
||||||
# This is called to create the redirect so the user can Authorize
|
|
||||||
# Home .
|
|
||||||
else:
|
|
||||||
|
|
||||||
redirect_uri = f"{get_url(hass)}{WINK_AUTH_CALLBACK_PATH}"
|
|
||||||
|
|
||||||
wink_auth_start_url = pywink.get_authorization_url(
|
|
||||||
config_file.get(CONF_CLIENT_ID), redirect_uri
|
|
||||||
)
|
|
||||||
hass.http.register_redirect(WINK_AUTH_START, wink_auth_start_url)
|
|
||||||
hass.http.register_view(
|
|
||||||
WinkAuthCallbackView(config, config_file, pywink.request_token)
|
|
||||||
)
|
|
||||||
_request_oauth_completion(hass, config)
|
|
||||||
return True
|
|
||||||
|
|
||||||
pywink.set_user_agent(USER_AGENT)
|
|
||||||
sub_details = pywink.get_subscription_details()
|
|
||||||
hass.data[DOMAIN]["pubnub"] = PubNubSubscriptionHandler(
|
|
||||||
sub_details[0], origin=sub_details[1]
|
|
||||||
)
|
|
||||||
|
|
||||||
def _subscribe():
|
|
||||||
hass.data[DOMAIN]["pubnub"].subscribe()
|
|
||||||
|
|
||||||
# Call subscribe after the user sets up wink via the configurator
|
|
||||||
# All other methods will complete setup before
|
|
||||||
# EVENT_HOMEASSISTANT_START is called meaning they
|
|
||||||
# will call subscribe via the method below. (start_subscription)
|
|
||||||
if hass.data[DOMAIN]["configurator"]:
|
|
||||||
_subscribe()
|
|
||||||
|
|
||||||
def keep_alive_call(event_time):
|
|
||||||
"""Call the Wink API endpoints to keep PubNub working."""
|
|
||||||
_LOGGER.info("Polling the Wink API to keep PubNub updates flowing")
|
|
||||||
pywink.set_user_agent(str(int(time.time())))
|
|
||||||
_temp_response = pywink.get_user()
|
|
||||||
_LOGGER.debug(str(json.dumps(_temp_response)))
|
|
||||||
time.sleep(1)
|
|
||||||
pywink.set_user_agent(USER_AGENT)
|
|
||||||
_temp_response = pywink.wink_api_fetch()
|
|
||||||
_LOGGER.debug("%s", _temp_response)
|
|
||||||
_temp_response = pywink.post_session()
|
|
||||||
_LOGGER.debug("%s", _temp_response)
|
|
||||||
|
|
||||||
# Call the Wink API every hour to keep PubNub updates flowing
|
|
||||||
track_time_interval(hass, keep_alive_call, timedelta(minutes=60))
|
|
||||||
|
|
||||||
def start_subscription(event):
|
|
||||||
"""Start the PubNub subscription."""
|
|
||||||
_subscribe()
|
|
||||||
|
|
||||||
hass.bus.listen(EVENT_HOMEASSISTANT_START, start_subscription)
|
|
||||||
|
|
||||||
def stop_subscription(event):
|
|
||||||
"""Stop the PubNub subscription."""
|
|
||||||
hass.data[DOMAIN]["pubnub"].unsubscribe()
|
|
||||||
hass.data[DOMAIN]["pubnub"] = None
|
|
||||||
|
|
||||||
hass.bus.listen(EVENT_HOMEASSISTANT_STOP, stop_subscription)
|
|
||||||
|
|
||||||
def save_credentials(event):
|
|
||||||
"""Save currently set OAuth credentials."""
|
|
||||||
if hass.data[DOMAIN]["oauth"].get("email") is None:
|
|
||||||
config_path = hass.config.path(WINK_CONFIG_FILE)
|
|
||||||
_config = pywink.get_current_oauth_credentials()
|
|
||||||
save_json(config_path, _config)
|
|
||||||
|
|
||||||
hass.bus.listen(EVENT_HOMEASSISTANT_STOP, save_credentials)
|
|
||||||
|
|
||||||
# Save the users potentially updated oauth credentials at a regular
|
|
||||||
# interval to prevent them from being expired after a HA reboot.
|
|
||||||
track_time_interval(hass, save_credentials, timedelta(minutes=60))
|
|
||||||
|
|
||||||
def force_update(call):
|
|
||||||
"""Force all devices to poll the Wink API."""
|
|
||||||
_LOGGER.info("Refreshing Wink states from API")
|
|
||||||
for entity_list in hass.data[DOMAIN]["entities"].values():
|
|
||||||
# Throttle the calls to Wink API
|
|
||||||
for entity in entity_list:
|
|
||||||
time.sleep(1)
|
|
||||||
entity.schedule_update_ha_state(True)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_REFRESH_STATES, force_update)
|
|
||||||
|
|
||||||
def pull_new_devices(call):
|
|
||||||
"""Pull new devices added to users Wink account since startup."""
|
|
||||||
_LOGGER.info("Getting new devices from Wink API")
|
|
||||||
for _component in WINK_COMPONENTS:
|
|
||||||
discovery.load_platform(hass, _component, DOMAIN, {}, config)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_ADD_NEW_DEVICES, pull_new_devices)
|
|
||||||
|
|
||||||
def set_pairing_mode(call):
|
|
||||||
"""Put the hub in provided pairing mode."""
|
|
||||||
hub_name = call.data.get("hub_name")
|
|
||||||
pairing_mode = call.data.get("pairing_mode")
|
|
||||||
kidde_code = call.data.get("kidde_radio_code")
|
|
||||||
for hub in WINK_HUBS:
|
|
||||||
if hub.name() == hub_name:
|
|
||||||
hub.pair_new_device(pairing_mode, kidde_radio_code=kidde_code)
|
|
||||||
|
|
||||||
def rename_device(call):
|
|
||||||
"""Set specified device's name."""
|
|
||||||
# This should only be called on one device at a time.
|
|
||||||
found_device = None
|
|
||||||
entity_id = call.data.get("entity_id")[0]
|
|
||||||
all_devices = []
|
|
||||||
for list_of_devices in hass.data[DOMAIN]["entities"].values():
|
|
||||||
all_devices += list_of_devices
|
|
||||||
for device in all_devices:
|
|
||||||
if device.entity_id == entity_id:
|
|
||||||
found_device = device
|
|
||||||
if found_device is not None:
|
|
||||||
name = call.data.get("name")
|
|
||||||
found_device.wink.set_name(name)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_RENAME_DEVICE, rename_device, schema=RENAME_DEVICE_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete_device(call):
|
|
||||||
"""Delete specified device."""
|
|
||||||
# This should only be called on one device at a time.
|
|
||||||
found_device = None
|
|
||||||
entity_id = call.data.get("entity_id")[0]
|
|
||||||
all_devices = []
|
|
||||||
for list_of_devices in hass.data[DOMAIN]["entities"].values():
|
|
||||||
all_devices += list_of_devices
|
|
||||||
for device in all_devices:
|
|
||||||
if device.entity_id == entity_id:
|
|
||||||
found_device = device
|
|
||||||
if found_device is not None:
|
|
||||||
found_device.wink.remove_device()
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_DELETE_DEVICE, delete_device, schema=DELETE_DEVICE_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
hubs = pywink.get_hubs()
|
|
||||||
for hub in hubs:
|
|
||||||
if hub.device_manufacturer() == "wink":
|
|
||||||
WINK_HUBS.append(hub)
|
|
||||||
|
|
||||||
if WINK_HUBS:
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_PAIRING_MODE,
|
|
||||||
set_pairing_mode,
|
|
||||||
schema=SET_PAIRING_MODE_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
def nimbus_service_handle(service):
|
|
||||||
"""Handle nimbus services."""
|
|
||||||
entity_id = service.data.get("entity_id")[0]
|
|
||||||
_all_dials = []
|
|
||||||
for sensor in hass.data[DOMAIN]["entities"]["sensor"]:
|
|
||||||
if isinstance(sensor, WinkNimbusDialDevice):
|
|
||||||
_all_dials.append(sensor)
|
|
||||||
for _dial in _all_dials:
|
|
||||||
if _dial.entity_id == entity_id:
|
|
||||||
if service.service == SERVICE_SET_DIAL_CONFIG:
|
|
||||||
_dial.set_configuration(**service.data)
|
|
||||||
if service.service == SERVICE_SET_DIAL_STATE:
|
|
||||||
_dial.wink.set_state(
|
|
||||||
service.data.get("value"), service.data.get("labels")
|
|
||||||
)
|
|
||||||
|
|
||||||
def siren_service_handle(service):
|
|
||||||
"""Handle siren services."""
|
|
||||||
entity_ids = service.data.get("entity_id")
|
|
||||||
all_sirens = []
|
|
||||||
for switch in hass.data[DOMAIN]["entities"]["switch"]:
|
|
||||||
if isinstance(switch, WinkSirenDevice):
|
|
||||||
all_sirens.append(switch)
|
|
||||||
sirens_to_set = []
|
|
||||||
if entity_ids is None:
|
|
||||||
sirens_to_set = all_sirens
|
|
||||||
else:
|
|
||||||
for siren in all_sirens:
|
|
||||||
if siren.entity_id in entity_ids:
|
|
||||||
sirens_to_set.append(siren)
|
|
||||||
|
|
||||||
for siren in sirens_to_set:
|
|
||||||
_man = siren.wink.device_manufacturer()
|
|
||||||
if (
|
|
||||||
service.service != SERVICE_SET_AUTO_SHUTOFF
|
|
||||||
and service.service != SERVICE_ENABLE_SIREN
|
|
||||||
and _man not in ("dome", "wink")
|
|
||||||
):
|
|
||||||
_LOGGER.error("Service only valid for Dome or Wink sirens")
|
|
||||||
return
|
|
||||||
|
|
||||||
if service.service == SERVICE_ENABLE_SIREN:
|
|
||||||
siren.wink.set_state(service.data.get(ATTR_ENABLED))
|
|
||||||
elif service.service == SERVICE_SET_AUTO_SHUTOFF:
|
|
||||||
siren.wink.set_auto_shutoff(service.data.get(ATTR_AUTO_SHUTOFF))
|
|
||||||
elif service.service == SERVICE_SET_CHIME_VOLUME:
|
|
||||||
siren.wink.set_chime_volume(service.data.get(ATTR_VOLUME))
|
|
||||||
elif service.service == SERVICE_SET_SIREN_VOLUME:
|
|
||||||
siren.wink.set_siren_volume(service.data.get(ATTR_VOLUME))
|
|
||||||
elif service.service == SERVICE_SET_SIREN_TONE:
|
|
||||||
siren.wink.set_siren_sound(service.data.get(ATTR_TONE))
|
|
||||||
elif service.service == SERVICE_ENABLE_CHIME:
|
|
||||||
siren.wink.set_chime(service.data.get(ATTR_TONE))
|
|
||||||
elif service.service == SERVICE_SIREN_STROBE_ENABLED:
|
|
||||||
siren.wink.set_siren_strobe_enabled(service.data.get(ATTR_ENABLED))
|
|
||||||
elif service.service == SERVICE_CHIME_STROBE_ENABLED:
|
|
||||||
siren.wink.set_chime_strobe_enabled(service.data.get(ATTR_ENABLED))
|
|
||||||
|
|
||||||
# Load components for the devices in Wink that we support
|
|
||||||
for wink_component in WINK_COMPONENTS:
|
|
||||||
hass.data[DOMAIN]["entities"][wink_component] = []
|
|
||||||
discovery.load_platform(hass, wink_component, DOMAIN, {}, config)
|
|
||||||
|
|
||||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
|
||||||
|
|
||||||
sirens = []
|
|
||||||
has_dome_or_wink_siren = False
|
|
||||||
for siren in pywink.get_sirens():
|
|
||||||
_man = siren.device_manufacturer()
|
|
||||||
if _man in ("dome", "wink"):
|
|
||||||
has_dome_or_wink_siren = True
|
|
||||||
_id = siren.object_id() + siren.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
sirens.append(WinkSirenDevice(siren, hass))
|
|
||||||
|
|
||||||
if sirens:
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_AUTO_SHUTOFF,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=SET_AUTO_SHUTOFF_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_ENABLE_SIREN,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=ENABLED_SIREN_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
if has_dome_or_wink_siren:
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_SIREN_TONE,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=SET_SIREN_TONE_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_ENABLE_CHIME,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=SET_CHIME_MODE_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_SIREN_VOLUME,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=SET_VOLUME_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_CHIME_VOLUME,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=SET_VOLUME_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SIREN_STROBE_ENABLED,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=SET_STROBE_ENABLED_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_CHIME_STROBE_ENABLED,
|
|
||||||
siren_service_handle,
|
|
||||||
schema=SET_STROBE_ENABLED_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
component.add_entities(sirens)
|
|
||||||
|
|
||||||
nimbi = []
|
|
||||||
dials = {}
|
|
||||||
all_nimbi = pywink.get_cloud_clocks()
|
|
||||||
all_dials = []
|
|
||||||
for nimbus in all_nimbi:
|
|
||||||
if nimbus.object_type() == "cloud_clock":
|
|
||||||
nimbi.append(nimbus)
|
|
||||||
dials[nimbus.object_id()] = []
|
|
||||||
for nimbus in all_nimbi:
|
|
||||||
if nimbus.object_type() == "dial":
|
|
||||||
dials[nimbus.parent_id()].append(nimbus)
|
|
||||||
|
|
||||||
for nimbus in nimbi:
|
|
||||||
for dial in dials[nimbus.object_id()]:
|
|
||||||
all_dials.append(WinkNimbusDialDevice(nimbus, dial, hass))
|
|
||||||
|
|
||||||
if nimbi:
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_DIAL_CONFIG,
|
|
||||||
nimbus_service_handle,
|
|
||||||
schema=DIAL_CONFIG_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_DIAL_STATE,
|
|
||||||
nimbus_service_handle,
|
|
||||||
schema=DIAL_STATE_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
component.add_entities(all_dials)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class WinkAuthCallbackView(HomeAssistantView):
|
|
||||||
"""Handle OAuth finish callback requests."""
|
|
||||||
|
|
||||||
url = "/auth/wink/callback"
|
|
||||||
name = "auth:wink:callback"
|
|
||||||
requires_auth = False
|
|
||||||
|
|
||||||
def __init__(self, config, config_file, request_token):
|
|
||||||
"""Initialize the OAuth callback view."""
|
|
||||||
self.config = config
|
|
||||||
self.config_file = config_file
|
|
||||||
self.request_token = request_token
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def get(self, request):
|
|
||||||
"""Finish OAuth callback request."""
|
|
||||||
hass = request.app["hass"]
|
|
||||||
data = request.query
|
|
||||||
|
|
||||||
response_message = """Wink has been successfully authorized!
|
|
||||||
You can close this window now! For the best results you should reboot
|
|
||||||
Home Assistant"""
|
|
||||||
html_response = """<html><head><title>Wink Auth</title></head>
|
|
||||||
<body><h1>{}</h1></body></html>"""
|
|
||||||
|
|
||||||
if data.get("code") is not None:
|
|
||||||
response = self.request_token(
|
|
||||||
data.get("code"), self.config_file[CONF_CLIENT_SECRET]
|
|
||||||
)
|
|
||||||
|
|
||||||
config_contents = {
|
|
||||||
ATTR_ACCESS_TOKEN: response["access_token"],
|
|
||||||
ATTR_REFRESH_TOKEN: response["refresh_token"],
|
|
||||||
CONF_CLIENT_ID: self.config_file[CONF_CLIENT_ID],
|
|
||||||
CONF_CLIENT_SECRET: self.config_file[CONF_CLIENT_SECRET],
|
|
||||||
}
|
|
||||||
save_json(hass.config.path(WINK_CONFIG_FILE), config_contents)
|
|
||||||
|
|
||||||
hass.async_add_job(setup, hass, self.config)
|
|
||||||
|
|
||||||
return Response(
|
|
||||||
text=html_response.format(response_message), content_type="text/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
error_msg = "No code returned from Wink API"
|
|
||||||
_LOGGER.error(error_msg)
|
|
||||||
return Response(text=html_response.format(error_msg), content_type="text/html")
|
|
||||||
|
|
||||||
|
|
||||||
class WinkDevice(Entity):
|
|
||||||
"""Representation a base Wink device."""
|
|
||||||
|
|
||||||
def __init__(self, wink, hass):
|
|
||||||
"""Initialize the Wink device."""
|
|
||||||
self.hass = hass
|
|
||||||
self.wink = wink
|
|
||||||
hass.data[DOMAIN]["pubnub"].add_subscription(
|
|
||||||
self.wink.pubnub_channel, self._pubnub_update
|
|
||||||
)
|
|
||||||
hass.data[DOMAIN]["unique_ids"].append(self.wink.object_id() + self.wink.name())
|
|
||||||
|
|
||||||
def _pubnub_update(self, message):
|
|
||||||
_LOGGER.debug(message)
|
|
||||||
try:
|
|
||||||
if message is None:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Error on pubnub update for %s polling API for current state",
|
|
||||||
self.name,
|
|
||||||
)
|
|
||||||
self.schedule_update_ha_state(True)
|
|
||||||
else:
|
|
||||||
self.wink.pubnub_update(message)
|
|
||||||
self.schedule_update_ha_state()
|
|
||||||
except (ValueError, KeyError, AttributeError):
|
|
||||||
_LOGGER.error(
|
|
||||||
"Error in pubnub JSON for %s polling API for current state", self.name
|
|
||||||
)
|
|
||||||
self.schedule_update_ha_state(True)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the device."""
|
|
||||||
return self.wink.name()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self):
|
|
||||||
"""Return the unique id of the Wink device."""
|
|
||||||
if hasattr(self.wink, "capability") and self.wink.capability() is not None:
|
|
||||||
return f"{self.wink.object_id()}_{self.wink.capability()}"
|
|
||||||
return self.wink.object_id()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def available(self):
|
|
||||||
"""Return true if connection == True."""
|
|
||||||
return self.wink.available()
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
"""Update state of the device."""
|
|
||||||
self.wink.update_state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def should_poll(self):
|
|
||||||
"""Only poll if we are not subscribed to pubnub."""
|
|
||||||
return self.wink.pubnub_channel is None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
attributes = {}
|
|
||||||
battery = self._battery_level
|
|
||||||
if battery:
|
|
||||||
attributes[ATTR_BATTERY_LEVEL] = battery
|
|
||||||
man_dev_model = self._manufacturer_device_model
|
|
||||||
if man_dev_model:
|
|
||||||
attributes["manufacturer_device_model"] = man_dev_model
|
|
||||||
man_dev_id = self._manufacturer_device_id
|
|
||||||
if man_dev_id:
|
|
||||||
attributes["manufacturer_device_id"] = man_dev_id
|
|
||||||
dev_man = self._device_manufacturer
|
|
||||||
if dev_man:
|
|
||||||
attributes["device_manufacturer"] = dev_man
|
|
||||||
model_name = self._model_name
|
|
||||||
if model_name:
|
|
||||||
attributes["model_name"] = model_name
|
|
||||||
tamper = self._tamper
|
|
||||||
if tamper is not None:
|
|
||||||
attributes["tamper_detected"] = tamper
|
|
||||||
return attributes
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _battery_level(self):
|
|
||||||
"""Return the battery level."""
|
|
||||||
if self.wink.battery_level() is not None:
|
|
||||||
return self.wink.battery_level() * 100
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _manufacturer_device_model(self):
|
|
||||||
"""Return the manufacturer device model."""
|
|
||||||
return self.wink.manufacturer_device_model()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _manufacturer_device_id(self):
|
|
||||||
"""Return the manufacturer device id."""
|
|
||||||
return self.wink.manufacturer_device_id()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _device_manufacturer(self):
|
|
||||||
"""Return the device manufacturer."""
|
|
||||||
return self.wink.device_manufacturer()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _model_name(self):
|
|
||||||
"""Return the model name."""
|
|
||||||
return self.wink.model_name()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _tamper(self):
|
|
||||||
"""Return the devices tamper status."""
|
|
||||||
if hasattr(self.wink, "tamper_detected"):
|
|
||||||
return self.wink.tamper_detected()
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class WinkSirenDevice(WinkDevice):
|
|
||||||
"""Representation of a Wink siren device."""
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["switch"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self):
|
|
||||||
"""Return sirens state."""
|
|
||||||
if self.wink.state():
|
|
||||||
return STATE_ON
|
|
||||||
return STATE_OFF
|
|
||||||
|
|
||||||
@property
|
|
||||||
def icon(self):
|
|
||||||
"""Return the icon to use in the frontend, if any."""
|
|
||||||
return "mdi:bell-ring"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device state attributes."""
|
|
||||||
attributes = super().extra_state_attributes
|
|
||||||
|
|
||||||
auto_shutoff = self.wink.auto_shutoff()
|
|
||||||
if auto_shutoff is not None:
|
|
||||||
attributes["auto_shutoff"] = auto_shutoff
|
|
||||||
|
|
||||||
siren_volume = self.wink.siren_volume()
|
|
||||||
if siren_volume is not None:
|
|
||||||
attributes["siren_volume"] = siren_volume
|
|
||||||
|
|
||||||
chime_volume = self.wink.chime_volume()
|
|
||||||
if chime_volume is not None:
|
|
||||||
attributes["chime_volume"] = chime_volume
|
|
||||||
|
|
||||||
strobe_enabled = self.wink.strobe_enabled()
|
|
||||||
if strobe_enabled is not None:
|
|
||||||
attributes["siren_strobe_enabled"] = strobe_enabled
|
|
||||||
|
|
||||||
chime_strobe_enabled = self.wink.chime_strobe_enabled()
|
|
||||||
if chime_strobe_enabled is not None:
|
|
||||||
attributes["chime_strobe_enabled"] = chime_strobe_enabled
|
|
||||||
|
|
||||||
siren_sound = self.wink.siren_sound()
|
|
||||||
if siren_sound is not None:
|
|
||||||
attributes["siren_sound"] = siren_sound
|
|
||||||
|
|
||||||
chime_mode = self.wink.chime_mode()
|
|
||||||
if chime_mode is not None:
|
|
||||||
attributes["chime_mode"] = chime_mode
|
|
||||||
|
|
||||||
return attributes
|
|
||||||
|
|
||||||
|
|
||||||
class WinkNimbusDialDevice(WinkDevice):
|
|
||||||
"""Representation of the Quirky Nimbus device."""
|
|
||||||
|
|
||||||
def __init__(self, nimbus, dial, hass):
|
|
||||||
"""Initialize the Nimbus dial."""
|
|
||||||
super().__init__(dial, hass)
|
|
||||||
self.parent = nimbus
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["sensor"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self):
|
|
||||||
"""Return dials current value."""
|
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the device."""
|
|
||||||
return f"{self.parent.name()} dial {self.wink.index() + 1}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device state attributes."""
|
|
||||||
attributes = super().extra_state_attributes
|
|
||||||
dial_attributes = self.dial_attributes()
|
|
||||||
|
|
||||||
return {**attributes, **dial_attributes}
|
|
||||||
|
|
||||||
def dial_attributes(self):
|
|
||||||
"""Return the dial only attributes."""
|
|
||||||
return {
|
|
||||||
"labels": self.wink.labels(),
|
|
||||||
"position": self.wink.position(),
|
|
||||||
"rotation": self.wink.rotation(),
|
|
||||||
"max_value": self.wink.max_value(),
|
|
||||||
"min_value": self.wink.min_value(),
|
|
||||||
"num_ticks": self.wink.ticks(),
|
|
||||||
"scale_type": self.wink.scale(),
|
|
||||||
"max_position": self.wink.max_position(),
|
|
||||||
"min_position": self.wink.min_position(),
|
|
||||||
}
|
|
||||||
|
|
||||||
def set_configuration(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Set the dial config.
|
|
||||||
|
|
||||||
Anything not sent will default to current setting.
|
|
||||||
"""
|
|
||||||
attributes = {**self.dial_attributes(), **kwargs}
|
|
||||||
|
|
||||||
min_value = attributes["min_value"]
|
|
||||||
max_value = attributes["max_value"]
|
|
||||||
rotation = attributes["rotation"]
|
|
||||||
ticks = attributes["num_ticks"]
|
|
||||||
scale = attributes["scale_type"]
|
|
||||||
min_position = attributes["min_position"]
|
|
||||||
max_position = attributes["max_position"]
|
|
||||||
|
|
||||||
self.wink.set_configuration(
|
|
||||||
min_value,
|
|
||||||
max_value,
|
|
||||||
rotation,
|
|
||||||
scale=scale,
|
|
||||||
ticks=ticks,
|
|
||||||
min_position=min_position,
|
|
||||||
max_position=max_position,
|
|
||||||
)
|
|
@ -1,75 +0,0 @@
|
|||||||
"""Support Wink alarm control panels."""
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
import homeassistant.components.alarm_control_panel as alarm
|
|
||||||
from homeassistant.components.alarm_control_panel.const import (
|
|
||||||
SUPPORT_ALARM_ARM_AWAY,
|
|
||||||
SUPPORT_ALARM_ARM_HOME,
|
|
||||||
)
|
|
||||||
from homeassistant.const import (
|
|
||||||
STATE_ALARM_ARMED_AWAY,
|
|
||||||
STATE_ALARM_ARMED_HOME,
|
|
||||||
STATE_ALARM_DISARMED,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
STATE_ALARM_PRIVACY = "Private"
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink platform."""
|
|
||||||
|
|
||||||
for camera in pywink.get_cameras():
|
|
||||||
# get_cameras returns multiple device types.
|
|
||||||
# Only add those that aren't sensors.
|
|
||||||
try:
|
|
||||||
camera.capability()
|
|
||||||
except AttributeError:
|
|
||||||
_id = camera.object_id() + camera.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkCameraDevice(camera, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanelEntity):
|
|
||||||
"""Representation a Wink camera alarm."""
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["alarm_control_panel"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self):
|
|
||||||
"""Return the state of the device."""
|
|
||||||
wink_state = self.wink.state()
|
|
||||||
if wink_state == "away":
|
|
||||||
state = STATE_ALARM_ARMED_AWAY
|
|
||||||
elif wink_state == "home":
|
|
||||||
state = STATE_ALARM_DISARMED
|
|
||||||
elif wink_state == "night":
|
|
||||||
state = STATE_ALARM_ARMED_HOME
|
|
||||||
else:
|
|
||||||
state = None
|
|
||||||
return state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self) -> int:
|
|
||||||
"""Return the list of supported features."""
|
|
||||||
return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY
|
|
||||||
|
|
||||||
def alarm_disarm(self, code=None):
|
|
||||||
"""Send disarm command."""
|
|
||||||
self.wink.set_mode("home")
|
|
||||||
|
|
||||||
def alarm_arm_home(self, code=None):
|
|
||||||
"""Send arm home command."""
|
|
||||||
self.wink.set_mode("night")
|
|
||||||
|
|
||||||
def alarm_arm_away(self, code=None):
|
|
||||||
"""Send arm away command."""
|
|
||||||
self.wink.set_mode("away")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
return {"private": self.wink.private()}
|
|
@ -1,197 +0,0 @@
|
|||||||
"""Support for Wink binary sensors."""
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
|
||||||
DEVICE_CLASS_MOISTURE,
|
|
||||||
DEVICE_CLASS_MOTION,
|
|
||||||
DEVICE_CLASS_OCCUPANCY,
|
|
||||||
DEVICE_CLASS_OPENING,
|
|
||||||
DEVICE_CLASS_SMOKE,
|
|
||||||
DEVICE_CLASS_SOUND,
|
|
||||||
DEVICE_CLASS_VIBRATION,
|
|
||||||
BinarySensorEntity,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# These are the available sensors mapped to binary_sensor class
|
|
||||||
SENSOR_TYPES = {
|
|
||||||
"brightness": "light",
|
|
||||||
"capturing_audio": DEVICE_CLASS_SOUND,
|
|
||||||
"capturing_video": None,
|
|
||||||
"co_detected": "gas",
|
|
||||||
"liquid_detected": DEVICE_CLASS_MOISTURE,
|
|
||||||
"loudness": DEVICE_CLASS_SOUND,
|
|
||||||
"motion": DEVICE_CLASS_MOTION,
|
|
||||||
"noise": DEVICE_CLASS_SOUND,
|
|
||||||
"opened": DEVICE_CLASS_OPENING,
|
|
||||||
"presence": DEVICE_CLASS_OCCUPANCY,
|
|
||||||
"smoke_detected": DEVICE_CLASS_SMOKE,
|
|
||||||
"vibration": DEVICE_CLASS_VIBRATION,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink binary sensor platform."""
|
|
||||||
|
|
||||||
for sensor in pywink.get_sensors():
|
|
||||||
_id = sensor.object_id() + sensor.name()
|
|
||||||
if (
|
|
||||||
_id not in hass.data[DOMAIN]["unique_ids"]
|
|
||||||
and sensor.capability() in SENSOR_TYPES
|
|
||||||
):
|
|
||||||
add_entities([WinkBinarySensorEntity(sensor, hass)])
|
|
||||||
|
|
||||||
for key in pywink.get_keys():
|
|
||||||
_id = key.object_id() + key.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkBinarySensorEntity(key, hass)])
|
|
||||||
|
|
||||||
for sensor in pywink.get_smoke_and_co_detectors():
|
|
||||||
_id = sensor.object_id() + sensor.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkSmokeDetector(sensor, hass)])
|
|
||||||
|
|
||||||
for hub in pywink.get_hubs():
|
|
||||||
_id = hub.object_id() + hub.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkHub(hub, hass)])
|
|
||||||
|
|
||||||
for remote in pywink.get_remotes():
|
|
||||||
_id = remote.object_id() + remote.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkRemote(remote, hass)])
|
|
||||||
|
|
||||||
for button in pywink.get_buttons():
|
|
||||||
_id = button.object_id() + button.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkButton(button, hass)])
|
|
||||||
|
|
||||||
for gang in pywink.get_gangs():
|
|
||||||
_id = gang.object_id() + gang.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkGang(gang, hass)])
|
|
||||||
|
|
||||||
for door_bell_sensor in pywink.get_door_bells():
|
|
||||||
_id = door_bell_sensor.object_id() + door_bell_sensor.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkBinarySensorEntity(door_bell_sensor, hass)])
|
|
||||||
|
|
||||||
for camera_sensor in pywink.get_cameras():
|
|
||||||
_id = camera_sensor.object_id() + camera_sensor.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
try:
|
|
||||||
if camera_sensor.capability() in SENSOR_TYPES:
|
|
||||||
add_entities([WinkBinarySensorEntity(camera_sensor, hass)])
|
|
||||||
except AttributeError:
|
|
||||||
_LOGGER.info("Device isn't a sensor, skipping")
|
|
||||||
|
|
||||||
|
|
||||||
class WinkBinarySensorEntity(WinkDevice, BinarySensorEntity):
|
|
||||||
"""Representation of a Wink binary sensor."""
|
|
||||||
|
|
||||||
def __init__(self, wink, hass):
|
|
||||||
"""Initialize the Wink binary sensor."""
|
|
||||||
super().__init__(wink, hass)
|
|
||||||
if hasattr(self.wink, "unit"):
|
|
||||||
self._unit_of_measurement = self.wink.unit()
|
|
||||||
else:
|
|
||||||
self._unit_of_measurement = None
|
|
||||||
if hasattr(self.wink, "capability"):
|
|
||||||
self.capability = self.wink.capability()
|
|
||||||
else:
|
|
||||||
self.capability = None
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["binary_sensor"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if the binary sensor is on."""
|
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this sensor, from DEVICE_CLASSES."""
|
|
||||||
return SENSOR_TYPES.get(self.capability)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device state attributes."""
|
|
||||||
return super().extra_state_attributes
|
|
||||||
|
|
||||||
|
|
||||||
class WinkSmokeDetector(WinkBinarySensorEntity):
|
|
||||||
"""Representation of a Wink Smoke detector."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device state attributes."""
|
|
||||||
_attributes = super().extra_state_attributes
|
|
||||||
_attributes["test_activated"] = self.wink.test_activated()
|
|
||||||
return _attributes
|
|
||||||
|
|
||||||
|
|
||||||
class WinkHub(WinkBinarySensorEntity):
|
|
||||||
"""Representation of a Wink Hub."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device state attributes."""
|
|
||||||
_attributes = super().extra_state_attributes
|
|
||||||
_attributes["update_needed"] = self.wink.update_needed()
|
|
||||||
_attributes["firmware_version"] = self.wink.firmware_version()
|
|
||||||
_attributes["pairing_mode"] = self.wink.pairing_mode()
|
|
||||||
_kidde_code = self.wink.kidde_radio_code()
|
|
||||||
if _kidde_code is not None:
|
|
||||||
# The service call to set the Kidde code
|
|
||||||
# takes a string of 1s and 0s so it makes
|
|
||||||
# sense to display it to the user that way
|
|
||||||
_formatted_kidde_code = f"{_kidde_code:b}".zfill(8)
|
|
||||||
_attributes["kidde_radio_code"] = _formatted_kidde_code
|
|
||||||
return _attributes
|
|
||||||
|
|
||||||
|
|
||||||
class WinkRemote(WinkBinarySensorEntity):
|
|
||||||
"""Representation of a Wink Lutron Connected bulb remote."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
_attributes = super().extra_state_attributes
|
|
||||||
_attributes["button_on_pressed"] = self.wink.button_on_pressed()
|
|
||||||
_attributes["button_off_pressed"] = self.wink.button_off_pressed()
|
|
||||||
_attributes["button_up_pressed"] = self.wink.button_up_pressed()
|
|
||||||
_attributes["button_down_pressed"] = self.wink.button_down_pressed()
|
|
||||||
return _attributes
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this sensor, from DEVICE_CLASSES."""
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class WinkButton(WinkBinarySensorEntity):
|
|
||||||
"""Representation of a Wink Relay button."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the device state attributes."""
|
|
||||||
_attributes = super().extra_state_attributes
|
|
||||||
_attributes["pressed"] = self.wink.pressed()
|
|
||||||
_attributes["long_pressed"] = self.wink.long_pressed()
|
|
||||||
return _attributes
|
|
||||||
|
|
||||||
|
|
||||||
class WinkGang(WinkBinarySensorEntity):
|
|
||||||
"""Representation of a Wink Relay gang."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if the gang is connected."""
|
|
||||||
return self.wink.state()
|
|
@ -1,520 +0,0 @@
|
|||||||
"""Support for Wink thermostats and Air Conditioners."""
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.climate import ClimateEntity
|
|
||||||
from homeassistant.components.climate.const import (
|
|
||||||
ATTR_TARGET_TEMP_HIGH,
|
|
||||||
ATTR_TARGET_TEMP_LOW,
|
|
||||||
CURRENT_HVAC_COOL,
|
|
||||||
CURRENT_HVAC_HEAT,
|
|
||||||
CURRENT_HVAC_IDLE,
|
|
||||||
CURRENT_HVAC_OFF,
|
|
||||||
FAN_AUTO,
|
|
||||||
FAN_HIGH,
|
|
||||||
FAN_LOW,
|
|
||||||
FAN_MEDIUM,
|
|
||||||
FAN_ON,
|
|
||||||
HVAC_MODE_AUTO,
|
|
||||||
HVAC_MODE_COOL,
|
|
||||||
HVAC_MODE_FAN_ONLY,
|
|
||||||
HVAC_MODE_HEAT,
|
|
||||||
HVAC_MODE_OFF,
|
|
||||||
PRESET_AWAY,
|
|
||||||
PRESET_ECO,
|
|
||||||
PRESET_NONE,
|
|
||||||
SUPPORT_AUX_HEAT,
|
|
||||||
SUPPORT_FAN_MODE,
|
|
||||||
SUPPORT_PRESET_MODE,
|
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
|
||||||
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
|
||||||
)
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS
|
|
||||||
from homeassistant.helpers.temperature import display_temp as show_temp
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
ATTR_ECO_TARGET = "eco_target"
|
|
||||||
ATTR_EXTERNAL_TEMPERATURE = "external_temperature"
|
|
||||||
ATTR_OCCUPIED = "occupied"
|
|
||||||
ATTR_SCHEDULE_ENABLED = "schedule_enabled"
|
|
||||||
ATTR_SMART_TEMPERATURE = "smart_temperature"
|
|
||||||
ATTR_TOTAL_CONSUMPTION = "total_consumption"
|
|
||||||
|
|
||||||
HA_HVAC_TO_WINK = {
|
|
||||||
HVAC_MODE_AUTO: "auto",
|
|
||||||
HVAC_MODE_COOL: "cool_only",
|
|
||||||
HVAC_MODE_FAN_ONLY: "fan_only",
|
|
||||||
HVAC_MODE_HEAT: "heat_only",
|
|
||||||
HVAC_MODE_OFF: "off",
|
|
||||||
}
|
|
||||||
|
|
||||||
WINK_HVAC_TO_HA = {value: key for key, value in HA_HVAC_TO_WINK.items()}
|
|
||||||
|
|
||||||
SUPPORT_FLAGS_THERMOSTAT = (
|
|
||||||
SUPPORT_TARGET_TEMPERATURE
|
|
||||||
| SUPPORT_TARGET_TEMPERATURE_RANGE
|
|
||||||
| SUPPORT_FAN_MODE
|
|
||||||
| SUPPORT_AUX_HEAT
|
|
||||||
)
|
|
||||||
SUPPORT_FAN_THERMOSTAT = [FAN_AUTO, FAN_ON]
|
|
||||||
SUPPORT_PRESET_THERMOSTAT = [PRESET_AWAY, PRESET_ECO]
|
|
||||||
|
|
||||||
SUPPORT_FLAGS_AC = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE | SUPPORT_PRESET_MODE
|
|
||||||
SUPPORT_FAN_AC = [FAN_HIGH, FAN_LOW, FAN_MEDIUM]
|
|
||||||
SUPPORT_PRESET_AC = [PRESET_NONE, PRESET_ECO]
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink climate devices."""
|
|
||||||
for climate in pywink.get_thermostats():
|
|
||||||
_id = climate.object_id() + climate.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkThermostat(climate, hass)])
|
|
||||||
for climate in pywink.get_air_conditioners():
|
|
||||||
_id = climate.object_id() + climate.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkAC(climate, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkThermostat(WinkDevice, ClimateEntity):
|
|
||||||
"""Representation of a Wink thermostat."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Return the list of supported features."""
|
|
||||||
return SUPPORT_FLAGS_THERMOSTAT
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["climate"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature_unit(self):
|
|
||||||
"""Return the unit of measurement."""
|
|
||||||
# The Wink API always returns temp in Celsius
|
|
||||||
return TEMP_CELSIUS
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the optional device state attributes."""
|
|
||||||
data = {}
|
|
||||||
if self.external_temperature is not None:
|
|
||||||
data[ATTR_EXTERNAL_TEMPERATURE] = show_temp(
|
|
||||||
self.hass,
|
|
||||||
self.external_temperature,
|
|
||||||
self.temperature_unit,
|
|
||||||
PRECISION_TENTHS,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.smart_temperature:
|
|
||||||
data[ATTR_SMART_TEMPERATURE] = self.smart_temperature
|
|
||||||
|
|
||||||
if self.occupied is not None:
|
|
||||||
data[ATTR_OCCUPIED] = self.occupied
|
|
||||||
|
|
||||||
if self.eco_target is not None:
|
|
||||||
data[ATTR_ECO_TARGET] = self.eco_target
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_temperature(self):
|
|
||||||
"""Return the current temperature."""
|
|
||||||
return self.wink.current_temperature()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_humidity(self):
|
|
||||||
"""Return the current humidity."""
|
|
||||||
if self.wink.current_humidity() is not None:
|
|
||||||
# The API states humidity will be a float 0-1
|
|
||||||
# the only example API response with humidity listed show an int
|
|
||||||
# This will address both possibilities
|
|
||||||
if self.wink.current_humidity() < 1:
|
|
||||||
return self.wink.current_humidity() * 100
|
|
||||||
return self.wink.current_humidity()
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def external_temperature(self):
|
|
||||||
"""Return the current external temperature."""
|
|
||||||
return self.wink.current_external_temperature()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def smart_temperature(self):
|
|
||||||
"""Return the current average temp of all remote sensor."""
|
|
||||||
return self.wink.current_smart_temperature()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def eco_target(self):
|
|
||||||
"""Return status of eco target (Is the thermostat in eco mode)."""
|
|
||||||
return self.wink.eco_target()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def occupied(self):
|
|
||||||
"""Return status of if the thermostat has detected occupancy."""
|
|
||||||
return self.wink.occupied()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def preset_mode(self):
|
|
||||||
"""Return the current preset mode, e.g., home, away, temp."""
|
|
||||||
mode = self.wink.current_hvac_mode()
|
|
||||||
if mode == "eco":
|
|
||||||
return PRESET_ECO
|
|
||||||
if self.wink.away():
|
|
||||||
return PRESET_AWAY
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def preset_modes(self):
|
|
||||||
"""Return a list of available preset modes."""
|
|
||||||
return SUPPORT_PRESET_THERMOSTAT
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_humidity(self):
|
|
||||||
"""Return the humidity we try to reach."""
|
|
||||||
target_hum = None
|
|
||||||
if self.wink.current_humidifier_mode() == "on":
|
|
||||||
if self.wink.current_humidifier_set_point() is not None:
|
|
||||||
target_hum = self.wink.current_humidifier_set_point() * 100
|
|
||||||
elif self.wink.current_dehumidifier_mode() == "on":
|
|
||||||
if self.wink.current_dehumidifier_set_point() is not None:
|
|
||||||
target_hum = self.wink.current_dehumidifier_set_point() * 100
|
|
||||||
else:
|
|
||||||
target_hum = None
|
|
||||||
return target_hum
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self):
|
|
||||||
"""Return the temperature we try to reach."""
|
|
||||||
if self.hvac_mode != HVAC_MODE_AUTO and not self.wink.away():
|
|
||||||
if self.hvac_mode == HVAC_MODE_COOL:
|
|
||||||
return self.wink.current_max_set_point()
|
|
||||||
if self.hvac_mode == HVAC_MODE_HEAT:
|
|
||||||
return self.wink.current_min_set_point()
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature_low(self):
|
|
||||||
"""Return the lower bound temperature we try to reach."""
|
|
||||||
if self.hvac_mode == HVAC_MODE_AUTO:
|
|
||||||
return self.wink.current_min_set_point()
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature_high(self):
|
|
||||||
"""Return the higher bound temperature we try to reach."""
|
|
||||||
if self.hvac_mode == HVAC_MODE_AUTO:
|
|
||||||
return self.wink.current_max_set_point()
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_aux_heat(self):
|
|
||||||
"""Return true if aux heater."""
|
|
||||||
if "aux" not in self.wink.hvac_modes():
|
|
||||||
return None
|
|
||||||
if self.wink.current_hvac_mode() == "aux":
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_mode(self) -> str:
|
|
||||||
"""Return hvac operation ie. heat, cool mode.
|
|
||||||
|
|
||||||
Need to be one of HVAC_MODE_*.
|
|
||||||
"""
|
|
||||||
if not self.wink.is_on():
|
|
||||||
return HVAC_MODE_OFF
|
|
||||||
|
|
||||||
wink_mode = self.wink.current_hvac_mode()
|
|
||||||
if wink_mode == "aux":
|
|
||||||
return HVAC_MODE_HEAT
|
|
||||||
if wink_mode == "eco":
|
|
||||||
return HVAC_MODE_AUTO
|
|
||||||
return WINK_HVAC_TO_HA.get(wink_mode, "")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_modes(self):
|
|
||||||
"""Return the list of available hvac operation modes.
|
|
||||||
|
|
||||||
Need to be a subset of HVAC_MODES.
|
|
||||||
"""
|
|
||||||
hvac_list = [HVAC_MODE_OFF]
|
|
||||||
|
|
||||||
modes = self.wink.hvac_modes()
|
|
||||||
for mode in modes:
|
|
||||||
if mode in ("eco", "aux"):
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
ha_mode = WINK_HVAC_TO_HA[mode]
|
|
||||||
hvac_list.append(ha_mode)
|
|
||||||
except KeyError:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Invalid operation mode mapping. %s doesn't map. "
|
|
||||||
"Please report this",
|
|
||||||
mode,
|
|
||||||
)
|
|
||||||
return hvac_list
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_action(self):
|
|
||||||
"""Return the current running hvac operation if supported.
|
|
||||||
|
|
||||||
Need to be one of CURRENT_HVAC_*.
|
|
||||||
"""
|
|
||||||
if not self.wink.is_on():
|
|
||||||
return CURRENT_HVAC_OFF
|
|
||||||
if self.wink.cool_on():
|
|
||||||
return CURRENT_HVAC_COOL
|
|
||||||
if self.wink.heat_on():
|
|
||||||
return CURRENT_HVAC_HEAT
|
|
||||||
return CURRENT_HVAC_IDLE
|
|
||||||
|
|
||||||
def set_temperature(self, **kwargs):
|
|
||||||
"""Set new target temperature."""
|
|
||||||
target_temp = kwargs.get(ATTR_TEMPERATURE)
|
|
||||||
target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
|
|
||||||
target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
|
|
||||||
if target_temp is not None:
|
|
||||||
if self.hvac_mode == HVAC_MODE_COOL:
|
|
||||||
target_temp_high = target_temp
|
|
||||||
if self.hvac_mode == HVAC_MODE_HEAT:
|
|
||||||
target_temp_low = target_temp
|
|
||||||
self.wink.set_temperature(target_temp_low, target_temp_high)
|
|
||||||
|
|
||||||
def set_hvac_mode(self, hvac_mode):
|
|
||||||
"""Set new target hvac mode."""
|
|
||||||
hvac_mode_to_set = HA_HVAC_TO_WINK.get(hvac_mode)
|
|
||||||
self.wink.set_operation_mode(hvac_mode_to_set)
|
|
||||||
|
|
||||||
def set_preset_mode(self, preset_mode):
|
|
||||||
"""Set new preset mode."""
|
|
||||||
# Away
|
|
||||||
if preset_mode != PRESET_AWAY and self.wink.away():
|
|
||||||
self.wink.set_away_mode(False)
|
|
||||||
elif preset_mode == PRESET_AWAY:
|
|
||||||
self.wink.set_away_mode()
|
|
||||||
|
|
||||||
if preset_mode == PRESET_ECO:
|
|
||||||
self.wink.set_operation_mode("eco")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fan_mode(self):
|
|
||||||
"""Return whether the fan is on."""
|
|
||||||
if self.wink.current_fan_mode() == "on":
|
|
||||||
return FAN_ON
|
|
||||||
if self.wink.current_fan_mode() == "auto":
|
|
||||||
return FAN_AUTO
|
|
||||||
# No Fan available so disable slider
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fan_modes(self):
|
|
||||||
"""List of available fan modes."""
|
|
||||||
if self.wink.has_fan():
|
|
||||||
return SUPPORT_FAN_THERMOSTAT
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_fan_mode(self, fan_mode):
|
|
||||||
"""Turn fan on/off."""
|
|
||||||
self.wink.set_fan_mode(fan_mode.lower())
|
|
||||||
|
|
||||||
def turn_aux_heat_on(self):
|
|
||||||
"""Turn auxiliary heater on."""
|
|
||||||
self.wink.set_operation_mode("aux")
|
|
||||||
|
|
||||||
def turn_aux_heat_off(self):
|
|
||||||
"""Turn auxiliary heater off."""
|
|
||||||
self.wink.set_operation_mode("heat_only")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def min_temp(self):
|
|
||||||
"""Return the minimum temperature."""
|
|
||||||
minimum = 7 # Default minimum
|
|
||||||
min_min = self.wink.min_min_set_point()
|
|
||||||
min_max = self.wink.min_max_set_point()
|
|
||||||
if self.hvac_mode == HVAC_MODE_HEAT:
|
|
||||||
if min_min:
|
|
||||||
return_value = min_min
|
|
||||||
else:
|
|
||||||
return_value = minimum
|
|
||||||
elif self.hvac_mode == HVAC_MODE_COOL:
|
|
||||||
if min_max:
|
|
||||||
return_value = min_max
|
|
||||||
else:
|
|
||||||
return_value = minimum
|
|
||||||
elif self.hvac_mode == HVAC_MODE_AUTO:
|
|
||||||
if min_min and min_max:
|
|
||||||
return_value = min(min_min, min_max)
|
|
||||||
else:
|
|
||||||
return_value = minimum
|
|
||||||
else:
|
|
||||||
return_value = minimum
|
|
||||||
return return_value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_temp(self):
|
|
||||||
"""Return the maximum temperature."""
|
|
||||||
maximum = 35 # Default maximum
|
|
||||||
max_min = self.wink.max_min_set_point()
|
|
||||||
max_max = self.wink.max_max_set_point()
|
|
||||||
if self.hvac_mode == HVAC_MODE_HEAT:
|
|
||||||
if max_min:
|
|
||||||
return_value = max_min
|
|
||||||
else:
|
|
||||||
return_value = maximum
|
|
||||||
elif self.hvac_mode == HVAC_MODE_COOL:
|
|
||||||
if max_max:
|
|
||||||
return_value = max_max
|
|
||||||
else:
|
|
||||||
return_value = maximum
|
|
||||||
elif self.hvac_mode == HVAC_MODE_AUTO:
|
|
||||||
if max_min and max_max:
|
|
||||||
return_value = min(max_min, max_max)
|
|
||||||
else:
|
|
||||||
return_value = maximum
|
|
||||||
else:
|
|
||||||
return_value = maximum
|
|
||||||
return return_value
|
|
||||||
|
|
||||||
|
|
||||||
class WinkAC(WinkDevice, ClimateEntity):
|
|
||||||
"""Representation of a Wink air conditioner."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Return the list of supported features."""
|
|
||||||
return SUPPORT_FLAGS_AC
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature_unit(self):
|
|
||||||
"""Return the unit of measurement."""
|
|
||||||
# The Wink API always returns temp in Celsius
|
|
||||||
return TEMP_CELSIUS
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the optional device state attributes."""
|
|
||||||
data = {}
|
|
||||||
data[ATTR_TOTAL_CONSUMPTION] = self.wink.total_consumption()
|
|
||||||
data[ATTR_SCHEDULE_ENABLED] = self.wink.schedule_enabled()
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_temperature(self):
|
|
||||||
"""Return the current temperature."""
|
|
||||||
return self.wink.current_temperature()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def preset_mode(self):
|
|
||||||
"""Return the current preset mode, e.g., home, away, temp."""
|
|
||||||
if not self.wink.is_on():
|
|
||||||
return PRESET_NONE
|
|
||||||
|
|
||||||
mode = self.wink.current_mode()
|
|
||||||
if mode == "auto_eco":
|
|
||||||
return PRESET_ECO
|
|
||||||
return PRESET_NONE
|
|
||||||
|
|
||||||
@property
|
|
||||||
def preset_modes(self):
|
|
||||||
"""Return a list of available preset modes."""
|
|
||||||
return SUPPORT_PRESET_AC
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_mode(self) -> str:
|
|
||||||
"""Return hvac operation ie. heat, cool mode.
|
|
||||||
|
|
||||||
Need to be one of HVAC_MODE_*.
|
|
||||||
"""
|
|
||||||
if not self.wink.is_on():
|
|
||||||
return HVAC_MODE_OFF
|
|
||||||
|
|
||||||
wink_mode = self.wink.current_mode()
|
|
||||||
if wink_mode == "auto_eco":
|
|
||||||
return HVAC_MODE_COOL
|
|
||||||
return WINK_HVAC_TO_HA.get(wink_mode, "")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hvac_modes(self):
|
|
||||||
"""Return the list of available hvac operation modes.
|
|
||||||
|
|
||||||
Need to be a subset of HVAC_MODES.
|
|
||||||
"""
|
|
||||||
hvac_list = [HVAC_MODE_OFF]
|
|
||||||
|
|
||||||
modes = self.wink.modes()
|
|
||||||
for mode in modes:
|
|
||||||
if mode == "auto_eco":
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
ha_mode = WINK_HVAC_TO_HA[mode]
|
|
||||||
hvac_list.append(ha_mode)
|
|
||||||
except KeyError:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Invalid operation mode mapping. %s doesn't map. "
|
|
||||||
"Please report this",
|
|
||||||
mode,
|
|
||||||
)
|
|
||||||
return hvac_list
|
|
||||||
|
|
||||||
def set_temperature(self, **kwargs):
|
|
||||||
"""Set new target temperature."""
|
|
||||||
target_temp = kwargs.get(ATTR_TEMPERATURE)
|
|
||||||
self.wink.set_temperature(target_temp)
|
|
||||||
|
|
||||||
def set_hvac_mode(self, hvac_mode):
|
|
||||||
"""Set new target hvac mode."""
|
|
||||||
hvac_mode_to_set = HA_HVAC_TO_WINK.get(hvac_mode)
|
|
||||||
self.wink.set_operation_mode(hvac_mode_to_set)
|
|
||||||
|
|
||||||
def set_preset_mode(self, preset_mode):
|
|
||||||
"""Set new preset mode."""
|
|
||||||
if preset_mode == PRESET_ECO:
|
|
||||||
self.wink.set_operation_mode("auto_eco")
|
|
||||||
elif self.hvac_mode == HVAC_MODE_COOL and preset_mode == PRESET_NONE:
|
|
||||||
self.set_hvac_mode(HVAC_MODE_COOL)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self):
|
|
||||||
"""Return the temperature we try to reach."""
|
|
||||||
return self.wink.current_max_set_point()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fan_mode(self):
|
|
||||||
"""
|
|
||||||
Return the current fan mode.
|
|
||||||
|
|
||||||
The official Wink app only supports 3 modes [low, medium, high]
|
|
||||||
which are equal to [0.33, 0.66, 1.0] respectively.
|
|
||||||
"""
|
|
||||||
speed = self.wink.current_fan_speed()
|
|
||||||
if speed <= 0.33:
|
|
||||||
return FAN_LOW
|
|
||||||
if speed <= 0.66:
|
|
||||||
return FAN_MEDIUM
|
|
||||||
return FAN_HIGH
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fan_modes(self):
|
|
||||||
"""Return a list of available fan modes."""
|
|
||||||
return SUPPORT_FAN_AC
|
|
||||||
|
|
||||||
def set_fan_mode(self, fan_mode):
|
|
||||||
"""
|
|
||||||
Set fan speed.
|
|
||||||
|
|
||||||
The official Wink app only supports 3 modes [low, medium, high]
|
|
||||||
which are equal to [0.33, 0.66, 1.0] respectively.
|
|
||||||
"""
|
|
||||||
if fan_mode == FAN_LOW:
|
|
||||||
speed = 0.33
|
|
||||||
elif fan_mode == FAN_MEDIUM:
|
|
||||||
speed = 0.66
|
|
||||||
elif fan_mode == FAN_HIGH:
|
|
||||||
speed = 1.0
|
|
||||||
self.wink.set_ac_fan_speed(speed)
|
|
@ -1,57 +0,0 @@
|
|||||||
"""Support for Wink covers."""
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.cover import ATTR_POSITION, CoverEntity
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink cover platform."""
|
|
||||||
|
|
||||||
for shade in pywink.get_shades():
|
|
||||||
_id = shade.object_id() + shade.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkCoverEntity(shade, hass)])
|
|
||||||
for shade in pywink.get_shade_groups():
|
|
||||||
_id = shade.object_id() + shade.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkCoverEntity(shade, hass)])
|
|
||||||
for door in pywink.get_garage_doors():
|
|
||||||
_id = door.object_id() + door.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkCoverEntity(door, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkCoverEntity(WinkDevice, CoverEntity):
|
|
||||||
"""Representation of a Wink cover device."""
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["cover"].append(self)
|
|
||||||
|
|
||||||
def close_cover(self, **kwargs):
|
|
||||||
"""Close the cover."""
|
|
||||||
self.wink.set_state(0)
|
|
||||||
|
|
||||||
def open_cover(self, **kwargs):
|
|
||||||
"""Open the cover."""
|
|
||||||
self.wink.set_state(1)
|
|
||||||
|
|
||||||
def set_cover_position(self, **kwargs):
|
|
||||||
"""Move the cover shutter to a specific position."""
|
|
||||||
position = kwargs.get(ATTR_POSITION)
|
|
||||||
self.wink.set_state(position / 100)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_cover_position(self):
|
|
||||||
"""Return the current position of cover shutter."""
|
|
||||||
if self.wink.state() is not None:
|
|
||||||
return int(self.wink.state() * 100)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_closed(self):
|
|
||||||
"""Return if the cover is closed."""
|
|
||||||
state = self.wink.state()
|
|
||||||
return bool(state == 0)
|
|
@ -1,112 +0,0 @@
|
|||||||
"""Support for Wink fans."""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.fan import (
|
|
||||||
SPEED_HIGH,
|
|
||||||
SPEED_LOW,
|
|
||||||
SPEED_MEDIUM,
|
|
||||||
SUPPORT_DIRECTION,
|
|
||||||
SUPPORT_SET_SPEED,
|
|
||||||
FanEntity,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
SPEED_AUTO = "auto"
|
|
||||||
SPEED_LOWEST = "lowest"
|
|
||||||
SUPPORTED_FEATURES = SUPPORT_DIRECTION + SUPPORT_SET_SPEED
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink platform."""
|
|
||||||
|
|
||||||
for fan in pywink.get_fans():
|
|
||||||
if fan.object_id() + fan.name() not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkFanDevice(fan, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkFanDevice(WinkDevice, FanEntity):
|
|
||||||
"""Representation of a Wink fan."""
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["fan"].append(self)
|
|
||||||
|
|
||||||
def set_direction(self, direction: str) -> None:
|
|
||||||
"""Set the direction of the fan."""
|
|
||||||
self.wink.set_fan_direction(direction)
|
|
||||||
|
|
||||||
def set_speed(self, speed: str) -> None:
|
|
||||||
"""Set the speed of the fan."""
|
|
||||||
self.wink.set_state(True, speed)
|
|
||||||
|
|
||||||
#
|
|
||||||
# The fan entity model has changed to use percentages and preset_modes
|
|
||||||
# instead of speeds.
|
|
||||||
#
|
|
||||||
# Please review
|
|
||||||
# https://developers.home-assistant.io/docs/core/entity/fan/
|
|
||||||
#
|
|
||||||
def turn_on(
|
|
||||||
self,
|
|
||||||
speed: str = None,
|
|
||||||
percentage: int = None,
|
|
||||||
preset_mode: str = None,
|
|
||||||
**kwargs,
|
|
||||||
) -> None:
|
|
||||||
"""Turn on the fan."""
|
|
||||||
self.wink.set_state(True, speed)
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs) -> None:
|
|
||||||
"""Turn off the fan."""
|
|
||||||
self.wink.set_state(False)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if the entity is on."""
|
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def speed(self) -> str | None:
|
|
||||||
"""Return the current speed."""
|
|
||||||
current_wink_speed = self.wink.current_fan_speed()
|
|
||||||
if SPEED_AUTO == current_wink_speed:
|
|
||||||
return SPEED_AUTO
|
|
||||||
if SPEED_LOWEST == current_wink_speed:
|
|
||||||
return SPEED_LOWEST
|
|
||||||
if SPEED_LOW == current_wink_speed:
|
|
||||||
return SPEED_LOW
|
|
||||||
if SPEED_MEDIUM == current_wink_speed:
|
|
||||||
return SPEED_MEDIUM
|
|
||||||
if SPEED_HIGH == current_wink_speed:
|
|
||||||
return SPEED_HIGH
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_direction(self):
|
|
||||||
"""Return direction of the fan [forward, reverse]."""
|
|
||||||
return self.wink.current_fan_direction()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def speed_list(self) -> list:
|
|
||||||
"""Get the list of available speeds."""
|
|
||||||
wink_supported_speeds = self.wink.fan_speeds()
|
|
||||||
supported_speeds = []
|
|
||||||
if SPEED_AUTO in wink_supported_speeds:
|
|
||||||
supported_speeds.append(SPEED_AUTO)
|
|
||||||
if SPEED_LOWEST in wink_supported_speeds:
|
|
||||||
supported_speeds.append(SPEED_LOWEST)
|
|
||||||
if SPEED_LOW in wink_supported_speeds:
|
|
||||||
supported_speeds.append(SPEED_LOW)
|
|
||||||
if SPEED_MEDIUM in wink_supported_speeds:
|
|
||||||
supported_speeds.append(SPEED_MEDIUM)
|
|
||||||
if SPEED_HIGH in wink_supported_speeds:
|
|
||||||
supported_speeds.append(SPEED_HIGH)
|
|
||||||
return supported_speeds
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self) -> int:
|
|
||||||
"""Flag supported features."""
|
|
||||||
return SUPPORTED_FEATURES
|
|
@ -1,114 +0,0 @@
|
|||||||
"""Support for Wink lights."""
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
|
||||||
ATTR_BRIGHTNESS,
|
|
||||||
ATTR_COLOR_TEMP,
|
|
||||||
ATTR_HS_COLOR,
|
|
||||||
SUPPORT_BRIGHTNESS,
|
|
||||||
SUPPORT_COLOR,
|
|
||||||
SUPPORT_COLOR_TEMP,
|
|
||||||
LightEntity,
|
|
||||||
)
|
|
||||||
from homeassistant.util import color as color_util
|
|
||||||
from homeassistant.util.color import (
|
|
||||||
color_temperature_mired_to_kelvin as mired_to_kelvin,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink lights."""
|
|
||||||
|
|
||||||
for light in pywink.get_light_bulbs():
|
|
||||||
_id = light.object_id() + light.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkLight(light, hass)])
|
|
||||||
for light in pywink.get_light_groups():
|
|
||||||
_id = light.object_id() + light.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkLight(light, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkLight(WinkDevice, LightEntity):
|
|
||||||
"""Representation of a Wink light."""
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["light"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if light is on."""
|
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def brightness(self):
|
|
||||||
"""Return the brightness of the light."""
|
|
||||||
if self.wink.brightness() is not None:
|
|
||||||
return int(self.wink.brightness() * 255)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hs_color(self):
|
|
||||||
"""Define current bulb color."""
|
|
||||||
if self.wink.supports_xy_color():
|
|
||||||
return color_util.color_xy_to_hs(*self.wink.color_xy())
|
|
||||||
|
|
||||||
if self.wink.supports_hue_saturation():
|
|
||||||
hue = self.wink.color_hue()
|
|
||||||
saturation = self.wink.color_saturation()
|
|
||||||
if hue is not None and saturation is not None:
|
|
||||||
return hue * 360, saturation * 100
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def color_temp(self):
|
|
||||||
"""Define current bulb color in degrees Kelvin."""
|
|
||||||
if not self.wink.supports_temperature():
|
|
||||||
return None
|
|
||||||
return color_util.color_temperature_kelvin_to_mired(
|
|
||||||
self.wink.color_temperature_kelvin()
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Flag supported features."""
|
|
||||||
supports = SUPPORT_BRIGHTNESS
|
|
||||||
if self.wink.supports_temperature():
|
|
||||||
supports = supports | SUPPORT_COLOR_TEMP
|
|
||||||
if self.wink.supports_xy_color():
|
|
||||||
supports = supports | SUPPORT_COLOR
|
|
||||||
elif self.wink.supports_hue_saturation():
|
|
||||||
supports = supports | SUPPORT_COLOR
|
|
||||||
return supports
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
|
||||||
"""Turn the switch on."""
|
|
||||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
|
||||||
hs_color = kwargs.get(ATTR_HS_COLOR)
|
|
||||||
color_temp_mired = kwargs.get(ATTR_COLOR_TEMP)
|
|
||||||
|
|
||||||
state_kwargs = {}
|
|
||||||
|
|
||||||
if hs_color:
|
|
||||||
if self.wink.supports_xy_color():
|
|
||||||
xy_color = color_util.color_hs_to_xy(*hs_color)
|
|
||||||
state_kwargs["color_xy"] = xy_color
|
|
||||||
if self.wink.supports_hue_saturation():
|
|
||||||
hs_scaled = hs_color[0] / 360, hs_color[1] / 100
|
|
||||||
state_kwargs["color_hue_saturation"] = hs_scaled
|
|
||||||
|
|
||||||
if color_temp_mired:
|
|
||||||
state_kwargs["color_kelvin"] = mired_to_kelvin(color_temp_mired)
|
|
||||||
|
|
||||||
if brightness:
|
|
||||||
state_kwargs["brightness"] = brightness / 255.0
|
|
||||||
|
|
||||||
self.wink.set_state(True, **state_kwargs)
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
|
||||||
"""Turn the switch off."""
|
|
||||||
self.wink.set_state(False)
|
|
@ -1,211 +0,0 @@
|
|||||||
"""Support for Wink locks."""
|
|
||||||
import pywink
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.components.lock import LockEntity
|
|
||||||
from homeassistant.const import (
|
|
||||||
ATTR_CODE,
|
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
ATTR_MODE,
|
|
||||||
ATTR_NAME,
|
|
||||||
STATE_UNKNOWN,
|
|
||||||
)
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
SERVICE_SET_VACATION_MODE = "set_lock_vacation_mode"
|
|
||||||
SERVICE_SET_ALARM_MODE = "set_lock_alarm_mode"
|
|
||||||
SERVICE_SET_ALARM_SENSITIVITY = "set_lock_alarm_sensitivity"
|
|
||||||
SERVICE_SET_ALARM_STATE = "set_lock_alarm_state"
|
|
||||||
SERVICE_SET_BEEPER_STATE = "set_lock_beeper_state"
|
|
||||||
SERVICE_ADD_KEY = "add_new_lock_key_code"
|
|
||||||
|
|
||||||
ATTR_ENABLED = "enabled"
|
|
||||||
ATTR_SENSITIVITY = "sensitivity"
|
|
||||||
|
|
||||||
ALARM_SENSITIVITY_MAP = {
|
|
||||||
"low": 0.2,
|
|
||||||
"medium_low": 0.4,
|
|
||||||
"medium": 0.6,
|
|
||||||
"medium_high": 0.8,
|
|
||||||
"high": 1.0,
|
|
||||||
}
|
|
||||||
|
|
||||||
ALARM_MODES_MAP = {
|
|
||||||
"activity": "alert",
|
|
||||||
"forced_entry": "forced_entry",
|
|
||||||
"tamper": "tamper",
|
|
||||||
}
|
|
||||||
|
|
||||||
SET_ENABLED_SCHEMA = vol.Schema(
|
|
||||||
{vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Required(ATTR_ENABLED): cv.string}
|
|
||||||
)
|
|
||||||
|
|
||||||
SET_SENSITIVITY_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
||||||
vol.Required(ATTR_SENSITIVITY): vol.In(ALARM_SENSITIVITY_MAP),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SET_ALARM_MODES_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
||||||
vol.Required(ATTR_MODE): vol.In(ALARM_MODES_MAP),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
ADD_KEY_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
||||||
vol.Required(ATTR_NAME): cv.string,
|
|
||||||
vol.Required(ATTR_CODE): cv.positive_int,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink platform."""
|
|
||||||
|
|
||||||
for lock in pywink.get_locks():
|
|
||||||
_id = lock.object_id() + lock.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkLockDevice(lock, hass)])
|
|
||||||
|
|
||||||
def service_handle(service):
|
|
||||||
"""Handle for services."""
|
|
||||||
entity_ids = service.data.get("entity_id")
|
|
||||||
all_locks = hass.data[DOMAIN]["entities"]["lock"]
|
|
||||||
locks_to_set = []
|
|
||||||
if entity_ids is None:
|
|
||||||
locks_to_set = all_locks
|
|
||||||
else:
|
|
||||||
for lock in all_locks:
|
|
||||||
if lock.entity_id in entity_ids:
|
|
||||||
locks_to_set.append(lock)
|
|
||||||
|
|
||||||
for lock in locks_to_set:
|
|
||||||
if service.service == SERVICE_SET_VACATION_MODE:
|
|
||||||
lock.set_vacation_mode(service.data.get(ATTR_ENABLED))
|
|
||||||
elif service.service == SERVICE_SET_ALARM_STATE:
|
|
||||||
lock.set_alarm_state(service.data.get(ATTR_ENABLED))
|
|
||||||
elif service.service == SERVICE_SET_BEEPER_STATE:
|
|
||||||
lock.set_beeper_state(service.data.get(ATTR_ENABLED))
|
|
||||||
elif service.service == SERVICE_SET_ALARM_MODE:
|
|
||||||
lock.set_alarm_mode(service.data.get(ATTR_MODE))
|
|
||||||
elif service.service == SERVICE_SET_ALARM_SENSITIVITY:
|
|
||||||
lock.set_alarm_sensitivity(service.data.get(ATTR_SENSITIVITY))
|
|
||||||
elif service.service == SERVICE_ADD_KEY:
|
|
||||||
name = service.data.get(ATTR_NAME)
|
|
||||||
code = service.data.get(ATTR_CODE)
|
|
||||||
lock.add_new_key(code, name)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_SET_VACATION_MODE, service_handle, schema=SET_ENABLED_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_SET_ALARM_STATE, service_handle, schema=SET_ENABLED_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_SET_BEEPER_STATE, service_handle, schema=SET_ENABLED_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_SET_ALARM_MODE, service_handle, schema=SET_ALARM_MODES_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_ALARM_SENSITIVITY,
|
|
||||||
service_handle,
|
|
||||||
schema=SET_SENSITIVITY_SCHEMA,
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_ADD_KEY, service_handle, schema=ADD_KEY_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class WinkLockDevice(WinkDevice, LockEntity):
|
|
||||||
"""Representation of a Wink lock."""
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["lock"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_locked(self):
|
|
||||||
"""Return true if device is locked."""
|
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
def lock(self, **kwargs):
|
|
||||||
"""Lock the device."""
|
|
||||||
self.wink.set_state(True)
|
|
||||||
|
|
||||||
def unlock(self, **kwargs):
|
|
||||||
"""Unlock the device."""
|
|
||||||
self.wink.set_state(False)
|
|
||||||
|
|
||||||
def set_alarm_state(self, enabled):
|
|
||||||
"""Set lock's alarm state."""
|
|
||||||
self.wink.set_alarm_state(enabled)
|
|
||||||
|
|
||||||
def set_vacation_mode(self, enabled):
|
|
||||||
"""Set lock's vacation mode."""
|
|
||||||
self.wink.set_vacation_mode(enabled)
|
|
||||||
|
|
||||||
def set_beeper_state(self, enabled):
|
|
||||||
"""Set lock's beeper mode."""
|
|
||||||
self.wink.set_beeper_mode(enabled)
|
|
||||||
|
|
||||||
def add_new_key(self, code, name):
|
|
||||||
"""Add a new user key code."""
|
|
||||||
self.wink.add_new_key(code, name)
|
|
||||||
|
|
||||||
def set_alarm_sensitivity(self, sensitivity):
|
|
||||||
"""
|
|
||||||
Set lock's alarm sensitivity.
|
|
||||||
|
|
||||||
Valid sensitivities:
|
|
||||||
0.2, 0.4, 0.6, 0.8, 1.0
|
|
||||||
"""
|
|
||||||
self.wink.set_alarm_sensitivity(sensitivity)
|
|
||||||
|
|
||||||
def set_alarm_mode(self, mode):
|
|
||||||
"""
|
|
||||||
Set lock's alarm mode.
|
|
||||||
|
|
||||||
Valid modes:
|
|
||||||
alert - Beep when lock is locked or unlocked
|
|
||||||
tamper - 15 sec alarm when lock is disturbed when locked
|
|
||||||
forced_entry - 3 min alarm when significant force applied
|
|
||||||
to door when locked.
|
|
||||||
"""
|
|
||||||
self.wink.set_alarm_mode(mode)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
super_attrs = super().extra_state_attributes
|
|
||||||
sensitivity = dict_value_to_key(
|
|
||||||
ALARM_SENSITIVITY_MAP, self.wink.alarm_sensitivity()
|
|
||||||
)
|
|
||||||
super_attrs["alarm_sensitivity"] = sensitivity
|
|
||||||
super_attrs["vacation_mode"] = self.wink.vacation_mode_enabled()
|
|
||||||
super_attrs["beeper_mode"] = self.wink.beeper_enabled()
|
|
||||||
super_attrs["auto_lock"] = self.wink.auto_lock_enabled()
|
|
||||||
alarm_mode = dict_value_to_key(ALARM_MODES_MAP, self.wink.alarm_mode())
|
|
||||||
super_attrs["alarm_mode"] = alarm_mode
|
|
||||||
super_attrs["alarm_enabled"] = self.wink.alarm_enabled()
|
|
||||||
return super_attrs
|
|
||||||
|
|
||||||
|
|
||||||
def dict_value_to_key(dict_map, comp_value):
|
|
||||||
"""Return the key that has the provided value."""
|
|
||||||
for key, value in dict_map.items():
|
|
||||||
if value == comp_value:
|
|
||||||
return key
|
|
||||||
return STATE_UNKNOWN
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"domain": "wink",
|
|
||||||
"name": "Wink",
|
|
||||||
"documentation": "https://www.home-assistant.io/integrations/wink",
|
|
||||||
"requirements": ["pubnubsub-handler==1.0.9", "python-wink==1.10.5"],
|
|
||||||
"dependencies": ["configurator", "http"],
|
|
||||||
"codeowners": [],
|
|
||||||
"iot_class": "cloud_polling"
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
"""Support for Wink scenes."""
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.scene import Scene
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink platform."""
|
|
||||||
|
|
||||||
for scene in pywink.get_scenes():
|
|
||||||
_id = scene.object_id() + scene.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkScene(scene, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkScene(WinkDevice, Scene):
|
|
||||||
"""Representation of a Wink shortcut/scene."""
|
|
||||||
|
|
||||||
def __init__(self, wink, hass):
|
|
||||||
"""Initialize the Wink device."""
|
|
||||||
super().__init__(wink, hass)
|
|
||||||
hass.data[DOMAIN]["entities"]["scene"].append(self)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["scene"].append(self)
|
|
||||||
|
|
||||||
def activate(self, **kwargs: Any) -> None:
|
|
||||||
"""Activate the scene."""
|
|
||||||
self.wink.activate()
|
|
@ -1,98 +0,0 @@
|
|||||||
"""Support for Wink sensors."""
|
|
||||||
from contextlib import suppress
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
|
||||||
from homeassistant.const import DEGREE, TEMP_CELSIUS
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
SENSOR_TYPES = ["temperature", "humidity", "balance", "proximity"]
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink platform."""
|
|
||||||
|
|
||||||
for sensor in pywink.get_sensors():
|
|
||||||
_id = sensor.object_id() + sensor.name()
|
|
||||||
if (
|
|
||||||
_id not in hass.data[DOMAIN]["unique_ids"]
|
|
||||||
and sensor.capability() in SENSOR_TYPES
|
|
||||||
):
|
|
||||||
add_entities([WinkSensorEntity(sensor, hass)])
|
|
||||||
|
|
||||||
for eggtray in pywink.get_eggtrays():
|
|
||||||
_id = eggtray.object_id() + eggtray.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkSensorEntity(eggtray, hass)])
|
|
||||||
|
|
||||||
for tank in pywink.get_propane_tanks():
|
|
||||||
_id = tank.object_id() + tank.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkSensorEntity(tank, hass)])
|
|
||||||
|
|
||||||
for piggy_bank in pywink.get_piggy_banks():
|
|
||||||
_id = piggy_bank.object_id() + piggy_bank.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
try:
|
|
||||||
if piggy_bank.capability() in SENSOR_TYPES:
|
|
||||||
add_entities([WinkSensorEntity(piggy_bank, hass)])
|
|
||||||
except AttributeError:
|
|
||||||
_LOGGER.info("Device is not a sensor")
|
|
||||||
|
|
||||||
|
|
||||||
class WinkSensorEntity(WinkDevice, SensorEntity):
|
|
||||||
"""Representation of a Wink sensor."""
|
|
||||||
|
|
||||||
def __init__(self, wink, hass):
|
|
||||||
"""Initialize the Wink device."""
|
|
||||||
super().__init__(wink, hass)
|
|
||||||
self.capability = self.wink.capability()
|
|
||||||
if self.wink.unit() == DEGREE:
|
|
||||||
self._unit_of_measurement = TEMP_CELSIUS
|
|
||||||
else:
|
|
||||||
self._unit_of_measurement = self.wink.unit()
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["sensor"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_value(self):
|
|
||||||
"""Return the state."""
|
|
||||||
state = None
|
|
||||||
if self.capability == "humidity":
|
|
||||||
if self.wink.state() is not None:
|
|
||||||
state = round(self.wink.state())
|
|
||||||
elif self.capability == "temperature":
|
|
||||||
if self.wink.state() is not None:
|
|
||||||
state = round(self.wink.state(), 1)
|
|
||||||
elif self.capability == "balance":
|
|
||||||
if self.wink.state() is not None:
|
|
||||||
state = round(self.wink.state() / 100, 2)
|
|
||||||
elif self.capability == "proximity":
|
|
||||||
if self.wink.state() is not None:
|
|
||||||
state = self.wink.state()
|
|
||||||
else:
|
|
||||||
state = self.wink.state()
|
|
||||||
return state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def native_unit_of_measurement(self):
|
|
||||||
"""Return the unit of measurement of this entity, if any."""
|
|
||||||
return self._unit_of_measurement
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
super_attrs = super().extra_state_attributes
|
|
||||||
|
|
||||||
# Ignore error, this sensor isn't an eggminder
|
|
||||||
with suppress(AttributeError):
|
|
||||||
super_attrs["egg_times"] = self.wink.eggs()
|
|
||||||
|
|
||||||
return super_attrs
|
|
@ -1,431 +0,0 @@
|
|||||||
# Describes the format for available Wink services
|
|
||||||
pair_new_device:
|
|
||||||
name: Pair new device
|
|
||||||
description: Pair a new device to a Wink Hub.
|
|
||||||
fields:
|
|
||||||
hub_name:
|
|
||||||
name: Hub name
|
|
||||||
description: The name of the hub to pair a new device to.
|
|
||||||
required: true
|
|
||||||
example: "My hub"
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
pairing_mode:
|
|
||||||
name: Pairing mode
|
|
||||||
description: Mode.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'bluetooth'
|
|
||||||
- 'kidde'
|
|
||||||
- 'lutron'
|
|
||||||
- 'zigbee'
|
|
||||||
- 'zwave'
|
|
||||||
- 'zwave_exclusion'
|
|
||||||
- 'zwave_network_rediscovery'
|
|
||||||
kidde_radio_code:
|
|
||||||
name: Kidde radio code
|
|
||||||
description: "A string of 8 1s and 0s one for each dip switch on the kidde device left --> right = 1 --> 8. Down = 1 and Up = 0"
|
|
||||||
example: "10101010"
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
|
|
||||||
rename_wink_device:
|
|
||||||
name: Rename wink device
|
|
||||||
description: Rename the provided device.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
fields:
|
|
||||||
name:
|
|
||||||
name: Name
|
|
||||||
description: The name to change it to.
|
|
||||||
required: true
|
|
||||||
example: back_door
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
|
|
||||||
delete_wink_device:
|
|
||||||
name: Delete wink device
|
|
||||||
description: Remove/unpair device from Wink.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
|
|
||||||
pull_newly_added_devices_from_wink:
|
|
||||||
name: Pull newly added devices from wink
|
|
||||||
description: Pull newly paired devices from Wink.
|
|
||||||
|
|
||||||
refresh_state_from_wink:
|
|
||||||
name: Refresh state from wink
|
|
||||||
description: Pull the latest states for every device.
|
|
||||||
|
|
||||||
set_siren_volume:
|
|
||||||
name: Set siren volume
|
|
||||||
description: Set the volume of the siren for a Dome siren/chime.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
volume:
|
|
||||||
name: Volume
|
|
||||||
description: Volume level.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'low'
|
|
||||||
- 'medium'
|
|
||||||
- 'high'
|
|
||||||
|
|
||||||
enable_chime:
|
|
||||||
name: Enable chime
|
|
||||||
description: Enable the chime of a Dome siren with the provided sound.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
tone:
|
|
||||||
name: Tone
|
|
||||||
description: >-
|
|
||||||
The tone to use for the chime.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'alert'
|
|
||||||
- 'beep'
|
|
||||||
- 'beep_beep'
|
|
||||||
- 'doorbell'
|
|
||||||
- 'doorbell_extended'
|
|
||||||
- 'evacuation'
|
|
||||||
- 'fur_elise'
|
|
||||||
- 'inactive'
|
|
||||||
- 'police_siren'
|
|
||||||
- 'rondo_alla_turca'
|
|
||||||
- 'william_tell'
|
|
||||||
|
|
||||||
set_siren_tone:
|
|
||||||
name: Set siren tone
|
|
||||||
description: Set the sound to use when the siren is enabled. (This doesn't enable the siren)
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
tone:
|
|
||||||
name: Tone
|
|
||||||
description: >-
|
|
||||||
The tone to use for the chime.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'alert'
|
|
||||||
- 'beep'
|
|
||||||
- 'beep_beep'
|
|
||||||
- 'doorbell'
|
|
||||||
- 'doorbell_extended'
|
|
||||||
- 'evacuation'
|
|
||||||
- 'fur_elise'
|
|
||||||
- 'inactive'
|
|
||||||
- 'police_siren'
|
|
||||||
- 'rondo_alla_turca'
|
|
||||||
- 'william_tell'
|
|
||||||
|
|
||||||
siren_set_auto_shutoff:
|
|
||||||
name: Siren set auto shutoff
|
|
||||||
description: How long to sound the siren before turning off.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
auto_shutoff:
|
|
||||||
name: Auto shutoff
|
|
||||||
description: >-
|
|
||||||
The time in seconds to sound the siren. (None and -1 are forever. Use None for gocontrol, and -1 for Dome)
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'None'
|
|
||||||
- '-1'
|
|
||||||
- '30'
|
|
||||||
- '60'
|
|
||||||
- '120'
|
|
||||||
|
|
||||||
set_siren_strobe_enabled:
|
|
||||||
name: Set siren strobe enabled
|
|
||||||
description: Enable or disable the strobe light when the siren is sounding.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
enabled:
|
|
||||||
name: Enabled
|
|
||||||
description: "True or False"
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
set_chime_strobe_enabled:
|
|
||||||
name: Set chime strobe enabled
|
|
||||||
description: Enable or disable the strobe light when the chime is sounding.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
enabled:
|
|
||||||
name: Enabled
|
|
||||||
description: "True or False"
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
enable_siren:
|
|
||||||
name: Enable siren
|
|
||||||
description: Enable/disable the siren.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
enabled:
|
|
||||||
name: Enabled
|
|
||||||
description: "true or false"
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
set_chime_volume:
|
|
||||||
name: Set chime volume
|
|
||||||
description: Set the volume of the chime for a Dome siren/chime.
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
volume:
|
|
||||||
name: Volume
|
|
||||||
description: Volume level.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'low'
|
|
||||||
- 'medium'
|
|
||||||
- 'high'
|
|
||||||
|
|
||||||
set_nimbus_dial_configuration:
|
|
||||||
name: Set nimbus dial configuration
|
|
||||||
description: Set the configuration of an individual nimbus dial
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: switch
|
|
||||||
fields:
|
|
||||||
rotation:
|
|
||||||
name: Rotation
|
|
||||||
description: Direction dial hand should spin.
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'cw'
|
|
||||||
- 'ccw'
|
|
||||||
ticks:
|
|
||||||
name: Ticks
|
|
||||||
description: Number of times the hand should move
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 0
|
|
||||||
max: 3600
|
|
||||||
scale:
|
|
||||||
name: Scale
|
|
||||||
description: How the dial should move in response to higher values.
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'linear'
|
|
||||||
- 'log'
|
|
||||||
min_value:
|
|
||||||
name: minimum value
|
|
||||||
description: The minimum value allowed to be set
|
|
||||||
example: 0
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
max_value:
|
|
||||||
name: Maximum value
|
|
||||||
description: The maximum value allowed to be set
|
|
||||||
example: 500
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
min_position:
|
|
||||||
name: Minimum position
|
|
||||||
description: The minimum position the dial hand can rotate to generally.
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 0
|
|
||||||
max: 360
|
|
||||||
max_position:
|
|
||||||
name: Maximum position
|
|
||||||
description: The maximum position the dial hand can rotate to generally.
|
|
||||||
selector:
|
|
||||||
number:
|
|
||||||
min: 0
|
|
||||||
max: 360
|
|
||||||
|
|
||||||
set_nimbus_dial_state:
|
|
||||||
name: Set nimbus dial state
|
|
||||||
description: Set the value and labels of an individual nimbus dial
|
|
||||||
target:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
fields:
|
|
||||||
value:
|
|
||||||
name: Value
|
|
||||||
description: The value that should be set (Should be between min_value and max_value)
|
|
||||||
required: true
|
|
||||||
example: 250
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
labels:
|
|
||||||
name: Labels
|
|
||||||
description: >-
|
|
||||||
The values shown on the dial labels ["Dial 1", "test"] the first value
|
|
||||||
is what is shown by default the second value is shown when the nimbus is
|
|
||||||
pressed.
|
|
||||||
example: ["example", "test"]
|
|
||||||
selector:
|
|
||||||
object:
|
|
||||||
|
|
||||||
set_lock_vacation_mode:
|
|
||||||
name: Set lock vacation mode
|
|
||||||
description: Set vacation mode for all or specified locks. Disables all user codes.
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
name: Entity
|
|
||||||
description: Name of lock to unlock.
|
|
||||||
selector:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: lock
|
|
||||||
enabled:
|
|
||||||
name: Enabled
|
|
||||||
description: enable or disable. true or false.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
set_lock_alarm_mode:
|
|
||||||
name: Set lock alarm mode
|
|
||||||
description: Set alarm mode for all or specified locks.
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
name: Entity
|
|
||||||
description: Name of lock to unlock.
|
|
||||||
selector:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: lock
|
|
||||||
mode:
|
|
||||||
name: Mode
|
|
||||||
description: Select mode.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'activity'
|
|
||||||
- 'forced_entry'
|
|
||||||
- 'tamper'
|
|
||||||
|
|
||||||
set_lock_alarm_sensitivity:
|
|
||||||
name: Set lock alarm sensitivity
|
|
||||||
description: Set alarm sensitivity for all or specified locks.
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
name: Entity
|
|
||||||
description: Name of lock to unlock.
|
|
||||||
selector:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: lock
|
|
||||||
sensitivity:
|
|
||||||
name: Sensitivity
|
|
||||||
description: Choose the sensitivity.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
select:
|
|
||||||
options:
|
|
||||||
- 'low'
|
|
||||||
- 'medium_low'
|
|
||||||
- 'medium'
|
|
||||||
- 'medium_high'
|
|
||||||
- 'high'
|
|
||||||
|
|
||||||
set_lock_alarm_state:
|
|
||||||
name: Set lok alarm state
|
|
||||||
description: Set alarm state.
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
name: Entity
|
|
||||||
description: Name of lock to unlock.
|
|
||||||
selector:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: lock
|
|
||||||
enabled:
|
|
||||||
name: Enabled
|
|
||||||
description: enable or disable.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
set_lock_beeper_state:
|
|
||||||
name: Set lock beeper state
|
|
||||||
description: Set beeper state.
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
name: Entity
|
|
||||||
description: Name of lock to unlock.
|
|
||||||
selector:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: lock
|
|
||||||
enabled:
|
|
||||||
name: Enabled
|
|
||||||
description: enable or disable.
|
|
||||||
required: true
|
|
||||||
selector:
|
|
||||||
boolean:
|
|
||||||
|
|
||||||
add_new_lock_key_code:
|
|
||||||
name: Add new lock key code
|
|
||||||
description: Add a new user key code.
|
|
||||||
fields:
|
|
||||||
entity_id:
|
|
||||||
name: Entity
|
|
||||||
description: Name of lock to unlock.
|
|
||||||
selector:
|
|
||||||
entity:
|
|
||||||
integration: wink
|
|
||||||
domain: lock
|
|
||||||
name:
|
|
||||||
name: Name
|
|
||||||
description: name of the new key code.
|
|
||||||
required: true
|
|
||||||
example: Bob
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
code:
|
|
||||||
name: Code
|
|
||||||
description: new key code, length must match length of other codes. Default length is 4.
|
|
||||||
required: true
|
|
||||||
example: 1234
|
|
||||||
selector:
|
|
||||||
text:
|
|
@ -1,60 +0,0 @@
|
|||||||
"""Support for Wink switches."""
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink platform."""
|
|
||||||
|
|
||||||
for switch in pywink.get_switches():
|
|
||||||
_id = switch.object_id() + switch.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkToggleDevice(switch, hass)])
|
|
||||||
for switch in pywink.get_powerstrips():
|
|
||||||
_id = switch.object_id() + switch.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkToggleDevice(switch, hass)])
|
|
||||||
for sprinkler in pywink.get_sprinklers():
|
|
||||||
_id = sprinkler.object_id() + sprinkler.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkToggleDevice(sprinkler, hass)])
|
|
||||||
for switch in pywink.get_binary_switch_groups():
|
|
||||||
_id = switch.object_id() + switch.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkToggleDevice(switch, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkToggleDevice(WinkDevice, ToggleEntity):
|
|
||||||
"""Representation of a Wink toggle device."""
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Call when entity is added to hass."""
|
|
||||||
self.hass.data[DOMAIN]["entities"]["switch"].append(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self):
|
|
||||||
"""Return true if device is on."""
|
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
|
||||||
"""Turn the device on."""
|
|
||||||
self.wink.set_state(True)
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
|
||||||
"""Turn the device off."""
|
|
||||||
self.wink.set_state(False)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
attributes = super().extra_state_attributes
|
|
||||||
try:
|
|
||||||
event = self.wink.last_event()
|
|
||||||
if event is not None:
|
|
||||||
attributes["last_event"] = event
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
return attributes
|
|
@ -1,143 +0,0 @@
|
|||||||
"""Support for Wink water heaters."""
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import pywink
|
|
||||||
|
|
||||||
from homeassistant.components.water_heater import (
|
|
||||||
ATTR_TEMPERATURE,
|
|
||||||
STATE_ECO,
|
|
||||||
STATE_ELECTRIC,
|
|
||||||
STATE_GAS,
|
|
||||||
STATE_HEAT_PUMP,
|
|
||||||
STATE_HIGH_DEMAND,
|
|
||||||
STATE_PERFORMANCE,
|
|
||||||
SUPPORT_AWAY_MODE,
|
|
||||||
SUPPORT_OPERATION_MODE,
|
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
|
||||||
WaterHeaterEntity,
|
|
||||||
)
|
|
||||||
from homeassistant.const import STATE_OFF, STATE_UNKNOWN, TEMP_CELSIUS
|
|
||||||
|
|
||||||
from . import DOMAIN, WinkDevice
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
SUPPORT_FLAGS_HEATER = (
|
|
||||||
SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE | SUPPORT_AWAY_MODE
|
|
||||||
)
|
|
||||||
|
|
||||||
ATTR_RHEEM_TYPE = "rheem_type"
|
|
||||||
ATTR_VACATION_MODE = "vacation_mode"
|
|
||||||
|
|
||||||
HA_STATE_TO_WINK = {
|
|
||||||
STATE_ECO: "eco",
|
|
||||||
STATE_ELECTRIC: "electric_only",
|
|
||||||
STATE_GAS: "gas",
|
|
||||||
STATE_HEAT_PUMP: "heat_pump",
|
|
||||||
STATE_HIGH_DEMAND: "high_demand",
|
|
||||||
STATE_OFF: "off",
|
|
||||||
STATE_PERFORMANCE: "performance",
|
|
||||||
}
|
|
||||||
|
|
||||||
WINK_STATE_TO_HA = {value: key for key, value in HA_STATE_TO_WINK.items()}
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
||||||
"""Set up the Wink water heater devices."""
|
|
||||||
|
|
||||||
for water_heater in pywink.get_water_heaters():
|
|
||||||
_id = water_heater.object_id() + water_heater.name()
|
|
||||||
if _id not in hass.data[DOMAIN]["unique_ids"]:
|
|
||||||
add_entities([WinkWaterHeater(water_heater, hass)])
|
|
||||||
|
|
||||||
|
|
||||||
class WinkWaterHeater(WinkDevice, WaterHeaterEntity):
|
|
||||||
"""Representation of a Wink water heater."""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Return the list of supported features."""
|
|
||||||
return SUPPORT_FLAGS_HEATER
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature_unit(self):
|
|
||||||
"""Return the unit of measurement."""
|
|
||||||
# The Wink API always returns temp in Celsius
|
|
||||||
return TEMP_CELSIUS
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_state_attributes(self):
|
|
||||||
"""Return the optional device state attributes."""
|
|
||||||
data = {}
|
|
||||||
data[ATTR_VACATION_MODE] = self.wink.vacation_mode_enabled()
|
|
||||||
data[ATTR_RHEEM_TYPE] = self.wink.rheem_type()
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_operation(self):
|
|
||||||
"""
|
|
||||||
Return current operation one of the following.
|
|
||||||
|
|
||||||
["eco", "performance", "heat_pump",
|
|
||||||
"high_demand", "electric_only", "gas]
|
|
||||||
"""
|
|
||||||
if not self.wink.is_on():
|
|
||||||
current_op = STATE_OFF
|
|
||||||
else:
|
|
||||||
current_op = WINK_STATE_TO_HA.get(self.wink.current_mode())
|
|
||||||
if current_op is None:
|
|
||||||
current_op = STATE_UNKNOWN
|
|
||||||
return current_op
|
|
||||||
|
|
||||||
@property
|
|
||||||
def operation_list(self):
|
|
||||||
"""List of available operation modes."""
|
|
||||||
op_list = ["off"]
|
|
||||||
modes = self.wink.modes()
|
|
||||||
for mode in modes:
|
|
||||||
if mode == "aux":
|
|
||||||
continue
|
|
||||||
ha_mode = WINK_STATE_TO_HA.get(mode)
|
|
||||||
if ha_mode is not None:
|
|
||||||
op_list.append(ha_mode)
|
|
||||||
else:
|
|
||||||
error = (
|
|
||||||
"Invalid operation mode mapping. "
|
|
||||||
f"{mode} doesn't map. Please report this."
|
|
||||||
)
|
|
||||||
_LOGGER.error(error)
|
|
||||||
return op_list
|
|
||||||
|
|
||||||
def set_temperature(self, **kwargs):
|
|
||||||
"""Set new target temperature."""
|
|
||||||
target_temp = kwargs.get(ATTR_TEMPERATURE)
|
|
||||||
self.wink.set_temperature(target_temp)
|
|
||||||
|
|
||||||
def set_operation_mode(self, operation_mode):
|
|
||||||
"""Set operation mode."""
|
|
||||||
op_mode_to_set = HA_STATE_TO_WINK.get(operation_mode)
|
|
||||||
self.wink.set_operation_mode(op_mode_to_set)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self):
|
|
||||||
"""Return the temperature we try to reach."""
|
|
||||||
return self.wink.current_set_point()
|
|
||||||
|
|
||||||
def turn_away_mode_on(self):
|
|
||||||
"""Turn away on."""
|
|
||||||
self.wink.set_vacation_mode(True)
|
|
||||||
|
|
||||||
def turn_away_mode_off(self):
|
|
||||||
"""Turn away off."""
|
|
||||||
self.wink.set_vacation_mode(False)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def min_temp(self):
|
|
||||||
"""Return the minimum temperature."""
|
|
||||||
return self.wink.min_set_point()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_temp(self):
|
|
||||||
"""Return the maximum temperature."""
|
|
||||||
return self.wink.max_set_point()
|
|
@ -1259,9 +1259,6 @@ proxmoxer==1.1.1
|
|||||||
# homeassistant.components.systemmonitor
|
# homeassistant.components.systemmonitor
|
||||||
psutil==5.8.0
|
psutil==5.8.0
|
||||||
|
|
||||||
# homeassistant.components.wink
|
|
||||||
pubnubsub-handler==1.0.9
|
|
||||||
|
|
||||||
# homeassistant.components.pulseaudio_loopback
|
# homeassistant.components.pulseaudio_loopback
|
||||||
pulsectl==20.2.4
|
pulsectl==20.2.4
|
||||||
|
|
||||||
@ -1948,9 +1945,6 @@ python-vlc==1.1.2
|
|||||||
# homeassistant.components.whois
|
# homeassistant.components.whois
|
||||||
python-whois==0.7.3
|
python-whois==0.7.3
|
||||||
|
|
||||||
# homeassistant.components.wink
|
|
||||||
python-wink==1.10.5
|
|
||||||
|
|
||||||
# homeassistant.components.awair
|
# homeassistant.components.awair
|
||||||
python_awair==0.2.1
|
python_awair==0.2.1
|
||||||
|
|
||||||
|
@ -62,7 +62,6 @@ ALLOWED_IGNORE_VIOLATIONS = {
|
|||||||
("velux", "scene.py"),
|
("velux", "scene.py"),
|
||||||
("wemo", "config_flow.py"),
|
("wemo", "config_flow.py"),
|
||||||
("wiffi", "config_flow.py"),
|
("wiffi", "config_flow.py"),
|
||||||
("wink", "scene.py"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user