From a050d5484754030b13e34be2fd636bcd4af91804 Mon Sep 17 00:00:00 2001
From: Paulus Schoutsen <balloob@gmail.com>
Date: Thu, 5 Dec 2019 01:15:28 -0800
Subject: [PATCH] Make hassfest stricter (#29494)

* Make hassfest stricter

* Update manifest.json
---
 .../components/ambiclimate/manifest.json      | 10 +--
 homeassistant/components/apns/manifest.json   |  5 +-
 homeassistant/components/camera/manifest.json |  1 +
 homeassistant/components/cloud/manifest.json  |  1 +
 .../components/doorbird/manifest.json         | 10 +--
 .../components/hikvisioncam/switch.py         |  5 +-
 homeassistant/components/html5/manifest.json  | 10 +--
 .../components/logbook/manifest.json          |  5 +-
 .../components/logi_circle/manifest.json      |  2 +-
 homeassistant/components/melissa/climate.py   | 19 +++---
 .../components/nsw_fuel_station/sensor.py     |  2 +-
 homeassistant/components/person/manifest.json |  1 +
 homeassistant/components/plant/manifest.json  | 10 +--
 homeassistant/components/point/manifest.json  | 12 +---
 homeassistant/components/rachio/manifest.json |  6 +-
 .../components/statistics/manifest.json       |  5 +-
 .../components/telegram_bot/__init__.py       |  5 +-
 homeassistant/components/tts/manifest.json    | 13 ++--
 homeassistant/components/wink/manifest.json   |  7 +--
 .../components/workday/binary_sensor.py       |  3 +-
 .../components/worxlandroid/sensor.py         |  2 +-
 script/hassfest/dependencies.py               | 62 ++++++++++++++++++-
 22 files changed, 112 insertions(+), 84 deletions(-)

diff --git a/homeassistant/components/ambiclimate/manifest.json b/homeassistant/components/ambiclimate/manifest.json
index 3d175165abd..151b761dff8 100644
--- a/homeassistant/components/ambiclimate/manifest.json
+++ b/homeassistant/components/ambiclimate/manifest.json
@@ -3,11 +3,7 @@
   "name": "Ambiclimate",
   "config_flow": true,
   "documentation": "https://www.home-assistant.io/integrations/ambiclimate",
-  "requirements": [
-    "ambiclimate==0.2.1"
-  ],
-  "dependencies": [],
-  "codeowners": [
-    "@danielhiversen"
-  ]
+  "requirements": ["ambiclimate==0.2.1"],
+  "dependencies": ["http"],
+  "codeowners": ["@danielhiversen"]
 }
diff --git a/homeassistant/components/apns/manifest.json b/homeassistant/components/apns/manifest.json
index 4845c45a963..3c38238f7eb 100644
--- a/homeassistant/components/apns/manifest.json
+++ b/homeassistant/components/apns/manifest.json
@@ -2,9 +2,8 @@
   "domain": "apns",
   "name": "Apns",
   "documentation": "https://www.home-assistant.io/integrations/apns",
-  "requirements": [
-    "apns2==0.3.0"
-  ],
+  "requirements": ["apns2==0.3.0"],
   "dependencies": [],
+  "after_dependencies": ["device_tracker"],
   "codeowners": []
 }
diff --git a/homeassistant/components/camera/manifest.json b/homeassistant/components/camera/manifest.json
index 1bd4bb7caeb..32cd7c3fe47 100644
--- a/homeassistant/components/camera/manifest.json
+++ b/homeassistant/components/camera/manifest.json
@@ -4,5 +4,6 @@
   "documentation": "https://www.home-assistant.io/integrations/camera",
   "requirements": [],
   "dependencies": ["http"],
+  "after_dependencies": ["stream", "media_player"],
   "codeowners": []
 }
diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json
index ec9a556af0a..accc4a0c0f9 100644
--- a/homeassistant/components/cloud/manifest.json
+++ b/homeassistant/components/cloud/manifest.json
@@ -4,5 +4,6 @@
   "documentation": "https://www.home-assistant.io/integrations/cloud",
   "requirements": ["hass-nabucasa==0.30"],
   "dependencies": ["http", "webhook"],
+  "after_dependencies": ["alexa", "google_assistant"],
   "codeowners": ["@home-assistant/cloud"]
 }
diff --git a/homeassistant/components/doorbird/manifest.json b/homeassistant/components/doorbird/manifest.json
index c9cdb32e18a..61225b86a44 100644
--- a/homeassistant/components/doorbird/manifest.json
+++ b/homeassistant/components/doorbird/manifest.json
@@ -2,11 +2,7 @@
   "domain": "doorbird",
   "name": "Doorbird",
   "documentation": "https://www.home-assistant.io/integrations/doorbird",
-  "requirements": [
-    "doorbirdpy==2.0.8"
-  ],
-  "dependencies": [],
-  "codeowners": [
-    "@oblogic7"
-  ]
+  "requirements": ["doorbirdpy==2.0.8"],
+  "dependencies": ["http"],
+  "codeowners": ["@oblogic7"]
 }
diff --git a/homeassistant/components/hikvisioncam/switch.py b/homeassistant/components/hikvisioncam/switch.py
index 020b894c0f7..f86853a5468 100644
--- a/homeassistant/components/hikvisioncam/switch.py
+++ b/homeassistant/components/hikvisioncam/switch.py
@@ -5,7 +5,7 @@ import hikvision.api
 from hikvision.error import HikvisionError, MissingParamError
 import voluptuous as vol
 
-from homeassistant.components.sensor import PLATFORM_SCHEMA
+from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
 from homeassistant.const import (
     CONF_HOST,
     CONF_NAME,
@@ -16,7 +16,6 @@ from homeassistant.const import (
     STATE_ON,
 )
 import homeassistant.helpers.config_validation as cv
-from homeassistant.helpers.entity import ToggleEntity
 
 # This is the last working version, please test before updating
 
@@ -60,7 +59,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
     add_entities([HikvisionMotionSwitch(name, hikvision_cam)])
 
 
-class HikvisionMotionSwitch(ToggleEntity):
+class HikvisionMotionSwitch(SwitchDevice):
     """Representation of a switch to toggle on/off motion detection."""
 
     def __init__(self, name, hikvision_cam):
diff --git a/homeassistant/components/html5/manifest.json b/homeassistant/components/html5/manifest.json
index 667a5789182..dd794ae0386 100644
--- a/homeassistant/components/html5/manifest.json
+++ b/homeassistant/components/html5/manifest.json
@@ -2,11 +2,7 @@
   "domain": "html5",
   "name": "HTML5 Notifications",
   "documentation": "https://www.home-assistant.io/integrations/html5",
-  "requirements": [
-    "pywebpush==1.9.2"
-  ],
-  "dependencies": ["frontend"],
-  "codeowners": [
-    "@robbiet480"
-  ]
+  "requirements": ["pywebpush==1.9.2"],
+  "dependencies": ["http"],
+  "codeowners": ["@robbiet480"]
 }
diff --git a/homeassistant/components/logbook/manifest.json b/homeassistant/components/logbook/manifest.json
index e8e3ad8ac2e..08083ea4024 100644
--- a/homeassistant/components/logbook/manifest.json
+++ b/homeassistant/components/logbook/manifest.json
@@ -3,9 +3,6 @@
   "name": "Logbook",
   "documentation": "https://www.home-assistant.io/integrations/logbook",
   "requirements": [],
-  "dependencies": [
-    "frontend",
-    "recorder"
-  ],
+  "dependencies": ["frontend", "http", "recorder"],
   "codeowners": []
 }
diff --git a/homeassistant/components/logi_circle/manifest.json b/homeassistant/components/logi_circle/manifest.json
index 22502956e06..bd6dc8a8d27 100644
--- a/homeassistant/components/logi_circle/manifest.json
+++ b/homeassistant/components/logi_circle/manifest.json
@@ -4,6 +4,6 @@
   "config_flow": true,
   "documentation": "https://www.home-assistant.io/integrations/logi_circle",
   "requirements": ["logi_circle==0.2.2"],
-  "dependencies": ["ffmpeg"],
+  "dependencies": ["ffmpeg", "http"],
   "codeowners": ["@evanjd"]
 }
diff --git a/homeassistant/components/melissa/climate.py b/homeassistant/components/melissa/climate.py
index 38f4977c96a..c09203c3e33 100644
--- a/homeassistant/components/melissa/climate.py
+++ b/homeassistant/components/melissa/climate.py
@@ -11,8 +11,11 @@ from homeassistant.components.climate.const import (
     HVAC_MODE_OFF,
     SUPPORT_FAN_MODE,
     SUPPORT_TARGET_TEMPERATURE,
+    FAN_AUTO,
+    FAN_HIGH,
+    FAN_MEDIUM,
+    FAN_LOW,
 )
-from homeassistant.components.fan import SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM
 from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
 
 from . import DATA_MELISSA
@@ -29,7 +32,7 @@ OP_MODES = [
     HVAC_MODE_OFF,
 ]
 
-FAN_MODES = [HVAC_MODE_AUTO, SPEED_HIGH, SPEED_MEDIUM, SPEED_LOW]
+FAN_MODES = [FAN_AUTO, FAN_HIGH, FAN_MEDIUM, FAN_LOW]
 
 
 async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
@@ -200,11 +203,11 @@ class MelissaClimate(ClimateDevice):
         if fan == self._api.FAN_AUTO:
             return HVAC_MODE_AUTO
         if fan == self._api.FAN_LOW:
-            return SPEED_LOW
+            return FAN_LOW
         if fan == self._api.FAN_MEDIUM:
-            return SPEED_MEDIUM
+            return FAN_MEDIUM
         if fan == self._api.FAN_HIGH:
-            return SPEED_HIGH
+            return FAN_HIGH
         _LOGGER.warning("Fan mode %s could not be mapped to hass", fan)
         return None
 
@@ -224,10 +227,10 @@ class MelissaClimate(ClimateDevice):
         """Translate hass fan modes to melissa modes."""
         if fan == HVAC_MODE_AUTO:
             return self._api.FAN_AUTO
-        if fan == SPEED_LOW:
+        if fan == FAN_LOW:
             return self._api.FAN_LOW
-        if fan == SPEED_MEDIUM:
+        if fan == FAN_MEDIUM:
             return self._api.FAN_MEDIUM
-        if fan == SPEED_HIGH:
+        if fan == FAN_HIGH:
             return self._api.FAN_HIGH
         _LOGGER.warning("Melissa have no setting for %s fan mode", fan)
diff --git a/homeassistant/components/nsw_fuel_station/sensor.py b/homeassistant/components/nsw_fuel_station/sensor.py
index a84aa554be9..3c900b46be0 100644
--- a/homeassistant/components/nsw_fuel_station/sensor.py
+++ b/homeassistant/components/nsw_fuel_station/sensor.py
@@ -6,7 +6,7 @@ from typing import Optional
 import voluptuous as vol
 
 import homeassistant.helpers.config_validation as cv
-from homeassistant.components.light import PLATFORM_SCHEMA
+from homeassistant.components.sensor import PLATFORM_SCHEMA
 from homeassistant.const import ATTR_ATTRIBUTION
 from homeassistant.helpers.entity import Entity
 from homeassistant.util import Throttle
diff --git a/homeassistant/components/person/manifest.json b/homeassistant/components/person/manifest.json
index cf50b8029c2..afcd428d6af 100644
--- a/homeassistant/components/person/manifest.json
+++ b/homeassistant/components/person/manifest.json
@@ -4,5 +4,6 @@
   "documentation": "https://www.home-assistant.io/integrations/person",
   "requirements": [],
   "dependencies": [],
+  "after_dependencies": ["device_tracker"],
   "codeowners": []
 }
diff --git a/homeassistant/components/plant/manifest.json b/homeassistant/components/plant/manifest.json
index 721a57e7822..c1e009ccec3 100644
--- a/homeassistant/components/plant/manifest.json
+++ b/homeassistant/components/plant/manifest.json
@@ -3,11 +3,7 @@
   "name": "Plant",
   "documentation": "https://www.home-assistant.io/integrations/plant",
   "requirements": [],
-  "dependencies": [
-    "group",
-    "zone"
-  ],
-  "codeowners": [
-    "@ChristianKuehnel"
-  ]
+  "dependencies": ["group", "zone"],
+  "after_dependencies": ["recorder"],
+  "codeowners": ["@ChristianKuehnel"]
 }
diff --git a/homeassistant/components/point/manifest.json b/homeassistant/components/point/manifest.json
index 4c29f37e67c..1c74052ee7e 100644
--- a/homeassistant/components/point/manifest.json
+++ b/homeassistant/components/point/manifest.json
@@ -3,13 +3,7 @@
   "name": "Point",
   "config_flow": true,
   "documentation": "https://www.home-assistant.io/integrations/point",
-  "requirements": [
-    "pypoint==1.1.2"
-  ],
-  "dependencies": [
-    "webhook"
-  ],
-  "codeowners": [
-    "@fredrike"
-  ]
+  "requirements": ["pypoint==1.1.2"],
+  "dependencies": ["webhook", "http"],
+  "codeowners": ["@fredrike"]
 }
diff --git a/homeassistant/components/rachio/manifest.json b/homeassistant/components/rachio/manifest.json
index 79e3677d65e..fae640f9262 100644
--- a/homeassistant/components/rachio/manifest.json
+++ b/homeassistant/components/rachio/manifest.json
@@ -2,9 +2,7 @@
   "domain": "rachio",
   "name": "Rachio",
   "documentation": "https://www.home-assistant.io/integrations/rachio",
-  "requirements": [
-    "rachiopy==0.1.3"
-  ],
-  "dependencies": [],
+  "requirements": ["rachiopy==0.1.3"],
+  "dependencies": ["http"],
   "codeowners": []
 }
diff --git a/homeassistant/components/statistics/manifest.json b/homeassistant/components/statistics/manifest.json
index 3dab05942b9..17ade1283ce 100644
--- a/homeassistant/components/statistics/manifest.json
+++ b/homeassistant/components/statistics/manifest.json
@@ -4,7 +4,6 @@
   "documentation": "https://www.home-assistant.io/integrations/statistics",
   "requirements": [],
   "dependencies": [],
-  "codeowners": [
-    "@fabaff"
-  ]
+  "after_dependencies": ["recorder"],
+  "codeowners": ["@fabaff"]
 }
diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py
index d365060e204..12bde6c72d8 100644
--- a/homeassistant/components/telegram_bot/__init__.py
+++ b/homeassistant/components/telegram_bot/__init__.py
@@ -19,7 +19,6 @@ from telegram.parsemode import ParseMode
 from telegram.utils.request import Request
 import voluptuous as vol
 
-from homeassistant.components.notify import ATTR_DATA, ATTR_MESSAGE, ATTR_TITLE
 from homeassistant.const import (
     ATTR_COMMAND,
     ATTR_LATITUDE,
@@ -35,6 +34,10 @@ from homeassistant.exceptions import TemplateError
 
 _LOGGER = logging.getLogger(__name__)
 
+ATTR_DATA = "data"
+ATTR_MESSAGE = "message"
+ATTR_TITLE = "title"
+
 ATTR_ARGS = "args"
 ATTR_AUTHENTICATION = "authentication"
 ATTR_CALLBACK_QUERY = "callback_query"
diff --git a/homeassistant/components/tts/manifest.json b/homeassistant/components/tts/manifest.json
index cb780523977..b57d5c36112 100644
--- a/homeassistant/components/tts/manifest.json
+++ b/homeassistant/components/tts/manifest.json
@@ -2,13 +2,8 @@
   "domain": "tts",
   "name": "Tts",
   "documentation": "https://www.home-assistant.io/integrations/tts",
-  "requirements": [
-    "mutagen==1.43.0"
-  ],
-  "dependencies": [
-    "http"
-  ],
-  "codeowners": [
-    "@robbiet480"
-  ]
+  "requirements": ["mutagen==1.43.0"],
+  "dependencies": ["http"],
+  "after_dependencies": ["media_player"],
+  "codeowners": ["@robbiet480"]
 }
diff --git a/homeassistant/components/wink/manifest.json b/homeassistant/components/wink/manifest.json
index acf9c38e632..a1bae648292 100644
--- a/homeassistant/components/wink/manifest.json
+++ b/homeassistant/components/wink/manifest.json
@@ -2,10 +2,7 @@
   "domain": "wink",
   "name": "Wink",
   "documentation": "https://www.home-assistant.io/integrations/wink",
-  "requirements": [
-    "pubnubsub-handler==1.0.8",
-    "python-wink==1.10.5"
-  ],
-  "dependencies": ["configurator"],
+  "requirements": ["pubnubsub-handler==1.0.8", "python-wink==1.10.5"],
+  "dependencies": ["configurator", "http"],
   "codeowners": []
 }
diff --git a/homeassistant/components/workday/binary_sensor.py b/homeassistant/components/workday/binary_sensor.py
index f95447c1e72..efa8b6ad77b 100644
--- a/homeassistant/components/workday/binary_sensor.py
+++ b/homeassistant/components/workday/binary_sensor.py
@@ -5,9 +5,8 @@ from datetime import datetime, timedelta
 import holidays
 import voluptuous as vol
 
-from homeassistant.components.sensor import PLATFORM_SCHEMA
 from homeassistant.const import CONF_NAME, WEEKDAYS
-from homeassistant.components.binary_sensor import BinarySensorDevice
+from homeassistant.components.binary_sensor import BinarySensorDevice, PLATFORM_SCHEMA
 import homeassistant.helpers.config_validation as cv
 
 _LOGGER = logging.getLogger(__name__)
diff --git a/homeassistant/components/worxlandroid/sensor.py b/homeassistant/components/worxlandroid/sensor.py
index 4e9bf0a6a4a..ad583d6d943 100644
--- a/homeassistant/components/worxlandroid/sensor.py
+++ b/homeassistant/components/worxlandroid/sensor.py
@@ -10,7 +10,7 @@ import voluptuous as vol
 import homeassistant.helpers.config_validation as cv
 
 from homeassistant.helpers.entity import Entity
-from homeassistant.components.switch import PLATFORM_SCHEMA
+from homeassistant.components.sensor import PLATFORM_SCHEMA
 from homeassistant.const import CONF_HOST, CONF_PIN, CONF_TIMEOUT
 from homeassistant.helpers.aiohttp_client import async_get_clientsession
 
diff --git a/script/hassfest/dependencies.py b/script/hassfest/dependencies.py
index e47deb76ad5..42a31f20610 100644
--- a/script/hassfest/dependencies.py
+++ b/script/hassfest/dependencies.py
@@ -16,7 +16,19 @@ def grep_dir(path: pathlib.Path, glob_pattern: str, search_pattern: str) -> Set[
             continue
 
         for match in pattern.finditer(fil.read_text()):
-            found.add(match.groups()[0])
+            integration = match.groups()[1]
+
+            if (
+                # If it's importing something from itself
+                integration == path.name
+                # Platform file
+                or (path / f"{integration}.py").exists()
+                # Dir for platform
+                or (path / integration).exists()
+            ):
+                continue
+
+            found.add(match.groups()[1])
 
     return found
 
@@ -30,19 +42,65 @@ ALLOWED_USED_COMPONENTS = {
     "hassio",
     "system_health",
     "websocket_api",
+    "automation",
+    "device_automation",
+    "zone",
+    "homeassistant",
+    "system_log",
+    "person",
+    # Discovery
+    "ssdp",
+    "discovery",
+    # Other
+    "mjpeg",  # base class, has no reqs or component to load.
 }
 
+IGNORE_VIOLATIONS = [
+    # Has same requirement, gets defaults.
+    ("sql", "recorder"),
+    # Sharing a base class
+    ("openalpr_cloud", "openalpr_local"),
+    ("lutron_caseta", "lutron"),
+    ("ffmpeg_noise", "ffmpeg_motion"),
+    # Demo
+    ("demo", "manual"),
+    ("demo", "openalpr_local"),
+    # This should become a helper method that integrations can submit data to
+    ("websocket_api", "lovelace"),
+    # Expose HA to external systems
+    "homekit",
+    "alexa",
+    "google_assistant",
+    "emulated_hue",
+    "prometheus",
+    "conversation",
+    "logbook",
+    # These should be extracted to external package
+    "pvoutput",
+    "dwd_weather_warnings",
+    # Should be rewritten to use own data fetcher
+    "scrape",
+]
+
 
 def validate_dependencies(integration: Integration):
     """Validate all dependencies."""
     # Find usage of hass.components
-    referenced = grep_dir(integration.path, "**/*.py", r"hass\.components\.(\w+)")
+    referenced = grep_dir(
+        integration.path, "**/*.py", r"(hass|homeassistant)\.components\.(\w+)"
+    )
     referenced -= ALLOWED_USED_COMPONENTS
     referenced -= set(integration.manifest["dependencies"])
     referenced -= set(integration.manifest.get("after_dependencies", []))
 
     if referenced:
         for domain in sorted(referenced):
+            if (
+                integration.domain in IGNORE_VIOLATIONS
+                or (integration.domain, domain) in IGNORE_VIOLATIONS
+            ):
+                continue
+
             integration.add_error(
                 "dependencies",
                 "Using component {} but it's not in 'dependencies' or 'after_dependencies'".format(
-- 
GitLab