From 45f3eb69910419f4708e5e5f57a1f37de26f8b90 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 14 Oct 2021 00:20:13 +0200 Subject: [PATCH] Remove deprecated Wink integration (#57634) --- .coveragerc | 1 - .../components/discovery/__init__.py | 2 - homeassistant/components/wink/__init__.py | 971 ------------------ .../components/wink/alarm_control_panel.py | 75 -- .../components/wink/binary_sensor.py | 197 ---- homeassistant/components/wink/climate.py | 520 ---------- homeassistant/components/wink/cover.py | 57 - homeassistant/components/wink/fan.py | 112 -- homeassistant/components/wink/light.py | 114 -- homeassistant/components/wink/lock.py | 211 ---- homeassistant/components/wink/manifest.json | 9 - homeassistant/components/wink/scene.py | 34 - homeassistant/components/wink/sensor.py | 98 -- homeassistant/components/wink/services.yaml | 431 -------- homeassistant/components/wink/switch.py | 60 -- homeassistant/components/wink/water_heater.py | 143 --- requirements_all.txt | 6 - script/hassfest/coverage.py | 1 - 18 files changed, 3042 deletions(-) delete mode 100644 homeassistant/components/wink/__init__.py delete mode 100644 homeassistant/components/wink/alarm_control_panel.py delete mode 100644 homeassistant/components/wink/binary_sensor.py delete mode 100644 homeassistant/components/wink/climate.py delete mode 100644 homeassistant/components/wink/cover.py delete mode 100644 homeassistant/components/wink/fan.py delete mode 100644 homeassistant/components/wink/light.py delete mode 100644 homeassistant/components/wink/lock.py delete mode 100644 homeassistant/components/wink/manifest.json delete mode 100644 homeassistant/components/wink/scene.py delete mode 100644 homeassistant/components/wink/sensor.py delete mode 100644 homeassistant/components/wink/services.yaml delete mode 100644 homeassistant/components/wink/switch.py delete mode 100644 homeassistant/components/wink/water_heater.py diff --git a/.coveragerc b/.coveragerc index 0f453518adc..18139bddf08 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1191,7 +1191,6 @@ omit = homeassistant/components/webostv/* homeassistant/components/whois/sensor.py homeassistant/components/wiffi/* - homeassistant/components/wink/* homeassistant/components/wirelesstag/* homeassistant/components/wolflink/__init__.py homeassistant/components/wolflink/sensor.py diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index bade569bb46..595771cd673 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -38,7 +38,6 @@ SERVICE_SAMSUNG_PRINTER = "samsung_printer" SERVICE_TELLDUSLIVE = "tellstick" SERVICE_YEELIGHT = "yeelight" SERVICE_WEMO = "belkin_wemo" -SERVICE_WINK = "wink" SERVICE_XIAOMI_GW = "xiaomi_gw" # These have custom protocols @@ -94,7 +93,6 @@ MIGRATED_SERVICE_HANDLERS = [ "sonos", "songpal", SERVICE_WEMO, - SERVICE_WINK, SERVICE_XIAOMI_GW, "volumio", SERVICE_YEELIGHT, diff --git a/homeassistant/components/wink/__init__.py b/homeassistant/components/wink/__init__.py deleted file mode 100644 index 702851a5e14..00000000000 --- a/homeassistant/components/wink/__init__.py +++ /dev/null @@ -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 = """Wink Auth -

{}

""" - - 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, - ) diff --git a/homeassistant/components/wink/alarm_control_panel.py b/homeassistant/components/wink/alarm_control_panel.py deleted file mode 100644 index 2f5ac83c6f5..00000000000 --- a/homeassistant/components/wink/alarm_control_panel.py +++ /dev/null @@ -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()} diff --git a/homeassistant/components/wink/binary_sensor.py b/homeassistant/components/wink/binary_sensor.py deleted file mode 100644 index 6a5977c1dc2..00000000000 --- a/homeassistant/components/wink/binary_sensor.py +++ /dev/null @@ -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() diff --git a/homeassistant/components/wink/climate.py b/homeassistant/components/wink/climate.py deleted file mode 100644 index 7836d71614f..00000000000 --- a/homeassistant/components/wink/climate.py +++ /dev/null @@ -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) diff --git a/homeassistant/components/wink/cover.py b/homeassistant/components/wink/cover.py deleted file mode 100644 index f2f4241c64d..00000000000 --- a/homeassistant/components/wink/cover.py +++ /dev/null @@ -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) diff --git a/homeassistant/components/wink/fan.py b/homeassistant/components/wink/fan.py deleted file mode 100644 index b918d596ef4..00000000000 --- a/homeassistant/components/wink/fan.py +++ /dev/null @@ -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 diff --git a/homeassistant/components/wink/light.py b/homeassistant/components/wink/light.py deleted file mode 100644 index 4d20cf4dd5a..00000000000 --- a/homeassistant/components/wink/light.py +++ /dev/null @@ -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) diff --git a/homeassistant/components/wink/lock.py b/homeassistant/components/wink/lock.py deleted file mode 100644 index 63a67d9f1ac..00000000000 --- a/homeassistant/components/wink/lock.py +++ /dev/null @@ -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 diff --git a/homeassistant/components/wink/manifest.json b/homeassistant/components/wink/manifest.json deleted file mode 100644 index e4da7b9c03a..00000000000 --- a/homeassistant/components/wink/manifest.json +++ /dev/null @@ -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" -} diff --git a/homeassistant/components/wink/scene.py b/homeassistant/components/wink/scene.py deleted file mode 100644 index 3f4724957f8..00000000000 --- a/homeassistant/components/wink/scene.py +++ /dev/null @@ -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() diff --git a/homeassistant/components/wink/sensor.py b/homeassistant/components/wink/sensor.py deleted file mode 100644 index 86199f44e91..00000000000 --- a/homeassistant/components/wink/sensor.py +++ /dev/null @@ -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 diff --git a/homeassistant/components/wink/services.yaml b/homeassistant/components/wink/services.yaml deleted file mode 100644 index 851f3bb9a43..00000000000 --- a/homeassistant/components/wink/services.yaml +++ /dev/null @@ -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: diff --git a/homeassistant/components/wink/switch.py b/homeassistant/components/wink/switch.py deleted file mode 100644 index d377ae0cddf..00000000000 --- a/homeassistant/components/wink/switch.py +++ /dev/null @@ -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 diff --git a/homeassistant/components/wink/water_heater.py b/homeassistant/components/wink/water_heater.py deleted file mode 100644 index bf5e8434746..00000000000 --- a/homeassistant/components/wink/water_heater.py +++ /dev/null @@ -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() diff --git a/requirements_all.txt b/requirements_all.txt index dcc9efa042f..42577015a37 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1259,9 +1259,6 @@ proxmoxer==1.1.1 # homeassistant.components.systemmonitor psutil==5.8.0 -# homeassistant.components.wink -pubnubsub-handler==1.0.9 - # homeassistant.components.pulseaudio_loopback pulsectl==20.2.4 @@ -1948,9 +1945,6 @@ python-vlc==1.1.2 # homeassistant.components.whois python-whois==0.7.3 -# homeassistant.components.wink -python-wink==1.10.5 - # homeassistant.components.awair python_awair==0.2.1 diff --git a/script/hassfest/coverage.py b/script/hassfest/coverage.py index 1a8609bb4e8..1e91edee90e 100644 --- a/script/hassfest/coverage.py +++ b/script/hassfest/coverage.py @@ -62,7 +62,6 @@ ALLOWED_IGNORE_VIOLATIONS = { ("velux", "scene.py"), ("wemo", "config_flow.py"), ("wiffi", "config_flow.py"), - ("wink", "scene.py"), }