コード例 #1
0
ファイル: test_network.py プロジェクト: jbouwh/core
def test_is_local():
    """Test local addresses."""
    assert network_util.is_local(ip_address("192.168.0.1"))
    assert network_util.is_local(ip_address("127.0.0.1"))
    assert network_util.is_local(ip_address("fd12:3456:789a:1::1"))
    assert network_util.is_local(ip_address("fe80::1234:5678:abcd"))
    assert network_util.is_local(ip_address("::ffff:192.168.0.1"))
    assert not network_util.is_local(ip_address("208.5.4.2"))
    assert not network_util.is_local(ip_address("198.51.100.1"))
    assert not network_util.is_local(ip_address("2001:DB8:FA1::1"))
    assert not network_util.is_local(ip_address("::ffff:208.5.4.2"))
コード例 #2
0
def _get_deprecated_base_url(
    hass: HomeAssistant,
    *,
    internal: bool = False,
    allow_ip: bool = True,
    require_current_request: bool = False,
    require_ssl: bool = False,
    require_standard_port: bool = False,
) -> str:
    """Work with the deprecated `base_url`, used as fallback."""
    if hass.config.api is None or not hass.config.api.deprecated_base_url:
        raise NoURLAvailableError

    base_url = yarl.URL(hass.config.api.deprecated_base_url)
    # Rules that apply to both internal and external
    if ((allow_ip or not is_ip_address(str(base_url.host))) and
        (not require_current_request or base_url.host == _get_request_host())
            and (not require_ssl or base_url.scheme == "https")
            and (not require_standard_port or base_url.is_default_port())):
        # Check to ensure an internal URL
        if internal and (str(base_url.host).endswith(".local") or
                         (is_ip_address(str(base_url.host))
                          and not is_loopback(ip_address(base_url.host))
                          and is_private(ip_address(base_url.host)))):
            return normalize_url(str(base_url))

        # Check to ensure an external URL (a little)
        if (not internal and not str(base_url.host).endswith(".local")
                and not (is_ip_address(str(base_url.host))
                         and is_local(ip_address(str(base_url.host))))):
            return normalize_url(str(base_url))

    raise NoURLAvailableError
コード例 #3
0
    async def get(self, request):
        """Handle a GET request."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        return self.json([{'success': {'username': '******'}}])
コード例 #4
0
    async def async_step_user(self, user_input=None):
        """Handle a user initiated set up flow."""
        if self._async_current_entries():
            return self.async_abort(reason='one_instance_allowed')

        try:
            url_parts = urlparse(self.hass.config.api.base_url)

            if is_local(ip_address(url_parts.hostname)):
                return self.async_abort(reason='not_internet_accessible')
        except ValueError:
            # If it's not an IP address, it's very likely publicly accessible
            pass

        if user_input is None:
            return self.async_show_form(
                step_id='user',
            )

        webhook_id = self.hass.components.webhook.async_generate_id()
        webhook_url = \
            self.hass.components.webhook.async_generate_url(webhook_id)

        return self.async_create_entry(
            title='IFTTT Webhook',
            data={
                CONF_WEBHOOK_ID: webhook_id
            },
            description_placeholders={
                'applet_url': 'https://ifttt.com/maker_webhooks',
                'webhook_url': webhook_url,
                'docs_url':
                'https://www.home-assistant.io/components/ifttt/'
            }
        )
コード例 #5
0
ファイル: hue_api.py プロジェクト: pkrolkgp/home-assistant
    def get(self, request, username, entity_id):
        """Process a request to get the state of an individual light."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("only local IPs allowed", HTTP_BAD_REQUEST)

        hass = request.app["hass"]
        hass_entity_id = self.config.number_to_entity_id(entity_id)

        if hass_entity_id is None:
            _LOGGER.error(
                "Unknown entity number: %s not found in emulated_hue_ids.json",
                entity_id,
            )
            return web.Response(text="Entity not found", status=404)

        entity = hass.states.get(hass_entity_id)

        if entity is None:
            _LOGGER.error("Entity not found: %s", hass_entity_id)
            return web.Response(text="Entity not found", status=404)

        if not self.config.is_entity_exposed(entity):
            _LOGGER.error("Entity not exposed: %s", entity_id)
            return web.Response(text="Entity not exposed", status=404)

        state = get_entity_state(self.config, entity)

        json_response = entity_to_json(self.config, entity, state)

        return self.json(json_response)
コード例 #6
0
    def get(self, request, username):
        """Process a request to get the list of available lights."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("Only local IPs allowed",
                                     HTTPStatus.UNAUTHORIZED)

        return self.json(create_list_of_entities(self.config, request))
コード例 #7
0
    async def async_step_user(self, user_input=None):
        """Handle a user initiated set up flow to create a webhook."""
        if not self._allow_multiple and self._async_current_entries():
            return self.async_abort(reason='one_instance_allowed')

        try:
            url_parts = urlparse(self.hass.config.api.base_url)

            if is_local(ip_address(url_parts.hostname)):
                return self.async_abort(reason='not_internet_accessible')
        except ValueError:
            # If it's not an IP address, it's very likely publicly accessible
            pass

        if user_input is None:
            return self.async_show_form(
                step_id='user',
            )

        webhook_id = self.hass.components.webhook.async_generate_id()
        webhook_url = \
            self.hass.components.webhook.async_generate_url(webhook_id)

        self._description_placeholder['webhook_url'] = webhook_url

        return self.async_create_entry(
            title=self._title,
            data={
                'webhook_id': webhook_id
            },
            description_placeholders=self._description_placeholder
        )
コード例 #8
0
    def get(self, request, username):
        """Process a request to get the list of available lights."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("Only local IPs allowed",
                                     HTTP_UNAUTHORIZED)

        return self.json(create_list_of_entities(self.config, request))
コード例 #9
0
    async def async_step_user(self, user_input=None):
        """Handle a user initiated set up flow to create a webhook."""
        if not self._allow_multiple and self._async_current_entries():
            return self.async_abort(reason='one_instance_allowed')

        try:
            url_parts = urlparse(self.hass.config.api.base_url)

            if is_local(ip_address(url_parts.hostname)):
                return self.async_abort(reason='not_internet_accessible')
        except ValueError:
            # If it's not an IP address, it's very likely publicly accessible
            pass

        if user_input is None:
            return self.async_show_form(step_id='user', )

        webhook_id = self.hass.components.webhook.async_generate_id()
        webhook_url = \
            self.hass.components.webhook.async_generate_url(webhook_id)

        self._description_placeholder['webhook_url'] = webhook_url

        return self.async_create_entry(
            title=self._title,
            data={'webhook_id': webhook_id},
            description_placeholders=self._description_placeholder)
コード例 #10
0
    def get(self, request, username):
        """Process a request to get the list of available lights."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("only local IPs allowed",
                                     HTTP_UNAUTHORIZED)
        if username != HUE_API_USERNAME:
            return self.json(UNAUTHORIZED_USER)

        json_response = {
            "lights": create_list_of_entities(self.config, request),
            "config": {
                "mac": "00:00:00:00:00:00",
                "swversion": "01003542",
                "apiversion": "1.17.0",
                "whitelist": {
                    HUE_API_USERNAME: {
                        "name": "HASS BRIDGE"
                    }
                },
                "ipaddress":
                f"{self.config.advertise_ip}:{self.config.advertise_port}",
                "linkbutton": True,
            },
        }

        return self.json(json_response)
コード例 #11
0
def async_user_not_allowed_do_auth(
        hass: HomeAssistant,
        user: User,
        request: Request | None = None) -> str | None:
    """Validate that user is not allowed to do auth things."""
    if not user.is_active:
        return "User is not active"

    if not user.local_only:
        return None

    # User is marked as local only, check if they are allowed to do auth
    if request is None:
        request = current_request.get()

    if not request:
        return "No request available to validate local access"

    if "cloud" in hass.config.components:
        # pylint: disable=import-outside-toplevel
        from hass_nabucasa import remote

        if remote.is_cloud_request.get():
            return "User is local only"

    try:
        remote = ip_address(request.remote)
    except ValueError:
        return "Invalid remote IP"

    if is_local(remote):
        return None

    return "User cannot authenticate remotely"
コード例 #12
0
    def get(self, request, username, entity_id):
        """Process a request to get the state of an individual light."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("Only local IPs allowed",
                                     HTTP_UNAUTHORIZED)

        hass = request.app["hass"]
        hass_entity_id = self.config.number_to_entity_id(entity_id)

        if hass_entity_id is None:
            _LOGGER.error(
                "Unknown entity number: %s not found in emulated_hue_ids.json",
                entity_id,
            )
            return self.json_message("Entity not found", HTTP_NOT_FOUND)

        entity = hass.states.get(hass_entity_id)

        if entity is None:
            _LOGGER.error("Entity not found: %s", hass_entity_id)
            return self.json_message("Entity not found", HTTP_NOT_FOUND)

        if not self.config.is_entity_exposed(entity):
            _LOGGER.error("Entity not exposed: %s", entity_id)
            return self.json_message("Entity not exposed", HTTP_UNAUTHORIZED)

        json_response = entity_to_json(self.config, entity)

        return self.json(json_response)
コード例 #13
0
    def get(self, request, username):
        """Process a request to make the Brilliant Lightpad work."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        return self.json({})
コード例 #14
0
    def get(self, request, username):
        """Process a request to make the Brilliant Lightpad work."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("Only local IPs allowed",
                                     HTTPStatus.UNAUTHORIZED)

        return self.json({})
コード例 #15
0
ファイル: network.py プロジェクト: zt17521/home-assistant
def async_get_external_url(hass: HomeAssistant) -> Optional[str]:
    """Get external url of this instance.

    Note: currently it takes 30 seconds after Home Assistant starts for
    cloud.async_remote_ui_url to work.
    """
    if "cloud" in hass.config.components:
        try:
            return cast(str, hass.components.cloud.async_remote_ui_url())
        except hass.components.cloud.CloudNotAvailable:
            pass

    if hass.config.api is None:
        return None

    base_url = yarl.URL(hass.config.api.base_url)

    try:
        if is_local(ip_address(base_url.host)):
            return None
    except ValueError:
        # ip_address raises ValueError if host is not an IP address
        pass

    return str(base_url)
コード例 #16
0
    def get(self, request, username):
        """Process a request to make the Brilliant Lightpad work."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("Only local IPs allowed",
                                     HTTP_UNAUTHORIZED)

        return self.json({})
コード例 #17
0
    def get(self, request, username=""):
        """Process a request to get the configuration."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("only local IPs allowed", HTTP_UNAUTHORIZED)

        json_response = create_config_model(self.config, request)

        return self.json(json_response)
コード例 #18
0
ファイル: hue_api.py プロジェクト: rikroe/core
    def get(self, request: web.Request, username: str) -> web.Response:
        """Process a request to make the Brilliant Lightpad work."""
        assert request.remote is not None
        if not is_local(ip_address(request.remote)):
            return self.json_message("Only local IPs allowed",
                                     HTTPStatus.UNAUTHORIZED)

        return self.json({})
コード例 #19
0
ファイル: hue_api.py プロジェクト: boced66/home-assistant
    def get(self, request, username):
        """Process a request to make the Brilliant Lightpad work."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        return self.json({
        })
コード例 #20
0
ファイル: hue_api.py プロジェクト: codacy-badger/core-1
    def get(self, request, username):
        """Process a request to get the configuration."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("only local IPs allowed", HTTP_UNAUTHORIZED)
        if username != HUE_API_USERNAME:
            return self.json(UNAUTHORIZED_USER)

        json_response = create_config_model(self.config, request)

        return self.json(json_response)
コード例 #21
0
ファイル: hue_api.py プロジェクト: rikroe/core
    def get(self, request: web.Request, username: str = "") -> web.Response:
        """Process a request to get the configuration."""
        assert request.remote is not None
        if not is_local(ip_address(request.remote)):
            return self.json_message("only local IPs allowed",
                                     HTTPStatus.UNAUTHORIZED)

        json_response = create_config_model(self.config, request)

        return self.json(json_response)
コード例 #22
0
ファイル: hue_api.py プロジェクト: boced66/home-assistant
    def put(self, request, username):
        """Process a request to make the Logitech Pop working."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        return self.json([{
            'error': {
                'address': '/groups/0/action/scene',
                'type': 7,
                'description': 'invalid value, dummy for parameter, scene'
            }
        }])
コード例 #23
0
    def put(self, request, username):
        """Process a request to make the Logitech Pop working."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("Only local IPs allowed",
                                     HTTPStatus.UNAUTHORIZED)

        return self.json([{
            "error": {
                "address": "/groups/0/action/scene",
                "type": 7,
                "description": "invalid value, dummy for parameter, scene",
            }
        }])
コード例 #24
0
    def put(self, request, username):
        """Process a request to make the Logitech Pop working."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        return self.json([{
            'error': {
                'address': '/groups/0/action/scene',
                'type': 7,
                'description': 'invalid value, dummy for parameter, scene'
            }
        }])
コード例 #25
0
    def get(self, request, username):
        """Process a request to get the list of available lights."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("only local IPs allowed", HTTP_UNAUTHORIZED)
        if username != HUE_API_USERNAME:
            return self.json(UNAUTHORIZED_USER)

        json_response = {
            "lights": create_list_of_entities(self.config, request),
            "config": create_config_model(self.config, request),
        }

        return self.json(json_response)
コード例 #26
0
ファイル: hue_api.py プロジェクト: kcsoft33/home-assistant-1
    def put(self, request, username):
        """Process a request to make the Logitech Pop working."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("only local IPs allowed",
                                     HTTP_BAD_REQUEST)

        return self.json([{
            "error": {
                "address": "/groups/0/action/scene",
                "type": 7,
                "description": "invalid value, dummy for parameter, scene",
            }
        }])
コード例 #27
0
    async def post(self, request):
        """Handle a POST request."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("Only local IPs allowed", HTTP_UNAUTHORIZED)

        try:
            data = await request.json()
        except ValueError:
            return self.json_message("Invalid JSON", HTTP_BAD_REQUEST)

        if "devicetype" not in data:
            return self.json_message("devicetype not specified", HTTP_BAD_REQUEST)

        return self.json([{"success": {"username": HUE_API_USERNAME}}])
コード例 #28
0
ファイル: hue_api.py プロジェクト: pkrolkgp/home-assistant
    async def post(self, request):
        """Handle a POST request."""
        try:
            data = await request.json()
        except ValueError:
            return self.json_message("Invalid JSON", HTTP_BAD_REQUEST)

        if "devicetype" not in data:
            return self.json_message("devicetype not specified", HTTP_BAD_REQUEST)

        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("only local IPs allowed", HTTP_BAD_REQUEST)

        return self.json([{"success": {"username": "******"}}])
コード例 #29
0
ファイル: hue_api.py プロジェクト: MumiLila/gittest4
    def get(self, request, username):
        """Process a request to get the list of available lights."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message("Only local IPs allowed",
                                     HTTP_UNAUTHORIZED)

        hass = request.app["hass"]
        json_response = {}

        for entity in hass.states.async_all():
            if self.config.is_entity_exposed(entity):
                number = self.config.entity_id_to_number(entity.entity_id)
                json_response[number] = entity_to_json(self.config, entity)

        return self.json(json_response)
コード例 #30
0
    async def put(self, request, username, entity_number):  # noqa: C901
        """Process a request to set the state of an individual light."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("Only local IPs allowed", HTTPStatus.UNAUTHORIZED)

        config = self.config
        hass = request.app["hass"]
        entity_id = config.number_to_entity_id(entity_number)

        if entity_id is None:
            _LOGGER.error("Unknown entity number: %s", entity_number)
            return self.json_message("Entity not found", HTTPStatus.NOT_FOUND)

        if (entity := hass.states.get(entity_id)) is None:
            _LOGGER.error("Entity not found: %s", entity_id)
            return self.json_message("Entity not found", HTTPStatus.NOT_FOUND)
コード例 #31
0
    async def post(self, request):
        """Handle a POST request."""
        try:
            data = await request.json()
        except ValueError:
            return self.json_message('Invalid JSON', HTTP_BAD_REQUEST)

        if 'devicetype' not in data:
            return self.json_message('devicetype not specified',
                                     HTTP_BAD_REQUEST)

        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        return self.json([{'success': {'username': '******'}}])
コード例 #32
0
ファイル: hue_api.py プロジェクト: boced66/home-assistant
    async def post(self, request):
        """Handle a POST request."""
        try:
            data = await request.json()
        except ValueError:
            return self.json_message('Invalid JSON', HTTP_BAD_REQUEST)

        if 'devicetype' not in data:
            return self.json_message('devicetype not specified',
                                     HTTP_BAD_REQUEST)

        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        return self.json([{'success': {'username': '******'}}])
コード例 #33
0
    def get(self, request, username):
        """Process a request to get the list of available lights."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        hass = request.app['hass']
        json_response = {}

        for entity in hass.states.async_all():
            if self.config.is_entity_exposed(entity):
                state, brightness = get_entity_state(self.config, entity)

                number = self.config.entity_id_to_number(entity.entity_id)
                json_response[number] = entity_to_json(self.config, entity,
                                                       state, brightness)

        return self.json(json_response)
コード例 #34
0
ファイル: hue_api.py プロジェクト: boced66/home-assistant
    def get(self, request, username):
        """Process a request to get the list of available lights."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        hass = request.app['hass']
        json_response = {}

        for entity in hass.states.async_all():
            if self.config.is_entity_exposed(entity):
                state, brightness = get_entity_state(self.config, entity)

                number = self.config.entity_id_to_number(entity.entity_id)
                json_response[number] = entity_to_json(
                    self.config, entity, state, brightness)

        return self.json(json_response)
コード例 #35
0
    def get(self, request, username, entity_id):
        """Process a request to get the state of an individual light."""
        if not is_local(ip_address(request.remote)):
            return self.json_message("Only local IPs allowed", HTTPStatus.UNAUTHORIZED)

        hass = request.app["hass"]
        hass_entity_id = self.config.number_to_entity_id(entity_id)

        if hass_entity_id is None:
            _LOGGER.error(
                "Unknown entity number: %s not found in emulated_hue_ids.json",
                entity_id,
            )
            return self.json_message("Entity not found", HTTPStatus.NOT_FOUND)

        if (entity := hass.states.get(hass_entity_id)) is None:
            _LOGGER.error("Entity not found: %s", hass_entity_id)
            return self.json_message("Entity not found", HTTPStatus.NOT_FOUND)
コード例 #36
0
ファイル: hue_api.py プロジェクト: boced66/home-assistant
    def get(self, request, username, entity_id):
        """Process a request to get the state of an individual light."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        hass = request.app['hass']
        entity_id = self.config.number_to_entity_id(entity_id)
        entity = hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error('Entity not found: %s', entity_id)
            return web.Response(text="Entity not found", status=404)

        if not self.config.is_entity_exposed(entity):
            _LOGGER.error('Entity not exposed: %s', entity_id)
            return web.Response(text="Entity not exposed", status=404)

        state, brightness = get_entity_state(self.config, entity)

        json_response = entity_to_json(self.config, entity, state, brightness)

        return self.json(json_response)
コード例 #37
0
ファイル: indieauth.py プロジェクト: boced66/home-assistant
def _parse_client_id(client_id):
    """Test if client id is a valid URL according to IndieAuth section 3.2.

    https://indieauth.spec.indieweb.org/#client-identifier
    """
    parts = _parse_url(client_id)

    # Client identifier URLs
    # MUST have either an https or http scheme
    if parts.scheme not in ('http', 'https'):
        raise ValueError()

    # MUST contain a path component
    # Handled by url canonicalization.

    # MUST NOT contain single-dot or double-dot path segments
    if any(segment in ('.', '..') for segment in parts.path.split('/')):
        raise ValueError(
            'Client ID cannot contain single-dot or double-dot path segments')

    # MUST NOT contain a fragment component
    if parts.fragment != '':
        raise ValueError('Client ID cannot contain a fragment')

    # MUST NOT contain a username or password component
    if parts.username is not None:
        raise ValueError('Client ID cannot contain username')

    if parts.password is not None:
        raise ValueError('Client ID cannot contain password')

    # MAY contain a port
    try:
        # parts raises ValueError when port cannot be parsed as int
        parts.port
    except ValueError:
        raise ValueError('Client ID contains invalid port')

    # Additionally, hostnames
    # MUST be domain names or a loopback interface and
    # MUST NOT be IPv4 or IPv6 addresses except for IPv4 127.0.0.1
    # or IPv6 [::1]

    # We are not goint to follow the spec here. We are going to allow
    # any internal network IP to be used inside a client id.

    address = None

    try:
        netloc = parts.netloc

        # Strip the [, ] from ipv6 addresses before parsing
        if netloc[0] == '[' and netloc[-1] == ']':
            netloc = netloc[1:-1]

        address = ip_address(netloc)
    except ValueError:
        # Not an ip address
        pass

    if address is None or is_local(address):
        return parts

    raise ValueError('Hostname should be a domain name or local IP address')
コード例 #38
0
ファイル: hue_api.py プロジェクト: boced66/home-assistant
    async def put(self, request, username, entity_number):
        """Process a request to set the state of an individual light."""
        if not is_local(request[KEY_REAL_IP]):
            return self.json_message('only local IPs allowed',
                                     HTTP_BAD_REQUEST)

        config = self.config
        hass = request.app['hass']
        entity_id = config.number_to_entity_id(entity_number)

        if entity_id is None:
            _LOGGER.error('Unknown entity number: %s', entity_number)
            return self.json_message('Entity not found', HTTP_NOT_FOUND)

        entity = hass.states.get(entity_id)

        if entity is None:
            _LOGGER.error('Entity not found: %s', entity_id)
            return self.json_message('Entity not found', HTTP_NOT_FOUND)

        if not config.is_entity_exposed(entity):
            _LOGGER.error('Entity not exposed: %s', entity_id)
            return web.Response(text="Entity not exposed", status=404)

        try:
            request_json = await request.json()
        except ValueError:
            _LOGGER.error('Received invalid json')
            return self.json_message('Invalid JSON', HTTP_BAD_REQUEST)

        # Parse the request into requested "on" status and brightness
        parsed = parse_hue_api_put_light_body(request_json, entity)

        if parsed is None:
            _LOGGER.error('Unable to parse data: %s', request_json)
            return web.Response(text="Bad request", status=400)

        result, brightness = parsed

        # Choose general HA domain
        domain = core.DOMAIN

        # Entity needs separate call to turn on
        turn_on_needed = False

        # Convert the resulting "on" status into the service we need to call
        service = SERVICE_TURN_ON if result else SERVICE_TURN_OFF

        # Construct what we need to send to the service
        data = {ATTR_ENTITY_ID: entity_id}

        # Make sure the entity actually supports brightness
        entity_features = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)

        if entity.domain == light.DOMAIN:
            if entity_features & SUPPORT_BRIGHTNESS:
                if brightness is not None:
                    data[ATTR_BRIGHTNESS] = brightness

        # If the requested entity is a script add some variables
        elif entity.domain == script.DOMAIN:
            data['variables'] = {
                'requested_state': STATE_ON if result else STATE_OFF
            }

            if brightness is not None:
                data['variables']['requested_level'] = brightness

        # If the requested entity is a climate, set the temperature
        elif entity.domain == climate.DOMAIN:
            # We don't support turning climate devices on or off,
            # only setting the temperature
            service = None

            if entity_features & SUPPORT_TARGET_TEMPERATURE:
                if brightness is not None:
                    domain = entity.domain
                    service = SERVICE_SET_TEMPERATURE
                    data[ATTR_TEMPERATURE] = brightness

        # If the requested entity is a media player, convert to volume
        elif entity.domain == media_player.DOMAIN:
            if entity_features & SUPPORT_VOLUME_SET:
                if brightness is not None:
                    turn_on_needed = True
                    domain = entity.domain
                    service = SERVICE_VOLUME_SET
                    # Convert 0-100 to 0.0-1.0
                    data[ATTR_MEDIA_VOLUME_LEVEL] = brightness / 100.0

        # If the requested entity is a cover, convert to open_cover/close_cover
        elif entity.domain == cover.DOMAIN:
            domain = entity.domain
            if service == SERVICE_TURN_ON:
                service = SERVICE_OPEN_COVER
            else:
                service = SERVICE_CLOSE_COVER

            if entity_features & SUPPORT_SET_POSITION:
                if brightness is not None:
                    domain = entity.domain
                    service = SERVICE_SET_COVER_POSITION
                    data[ATTR_POSITION] = brightness

        # If the requested entity is a fan, convert to speed
        elif entity.domain == fan.DOMAIN:
            if entity_features & SUPPORT_SET_SPEED:
                if brightness is not None:
                    domain = entity.domain
                    # Convert 0-100 to a fan speed
                    if brightness == 0:
                        data[ATTR_SPEED] = SPEED_OFF
                    elif 0 < brightness <= 33.3:
                        data[ATTR_SPEED] = SPEED_LOW
                    elif 33.3 < brightness <= 66.6:
                        data[ATTR_SPEED] = SPEED_MEDIUM
                    elif 66.6 < brightness <= 100:
                        data[ATTR_SPEED] = SPEED_HIGH

        if entity.domain in config.off_maps_to_on_domains:
            # Map the off command to on
            service = SERVICE_TURN_ON

            # Caching is required because things like scripts and scenes won't
            # report as "off" to Alexa if an "off" command is received, because
            # they'll map to "on". Thus, instead of reporting its actual
            # status, we report what Alexa will want to see, which is the same
            # as the actual requested command.
            config.cached_states[entity_id] = (result, brightness)

        # Separate call to turn on needed
        if turn_on_needed:
            hass.async_create_task(hass.services.async_call(
                core.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id},
                blocking=True))

        if service is not None:
            hass.async_create_task(hass.services.async_call(
                domain, service, data, blocking=True))

        json_response = \
            [create_hue_success_response(entity_id, HUE_API_STATE_ON, result)]

        if brightness is not None:
            json_response.append(create_hue_success_response(
                entity_id, HUE_API_STATE_BRI, brightness))

        return self.json(json_response)