예제 #1
0
async def _change_remote_access(hass, call):
    text = " zdalny dostęp do bramki z Internetu"
    access = hass.states.get("input_boolean.ais_remote_access").state
    # not allow to off on demo
    gate_id = ais_global.get_sercure_android_id_dom()
    if (ais_global.get_sercure_android_id_dom() in ("dom-demo", "dom-dev")
            and access != "on"):
        await hass.services.async_call(
            "ais_ai_service",
            "say_it",
            {
                "text":
                "Nie można zatrzymać zdalnego dostępu na instancji demo."
            },
        )
        hass.states.async_set("input_boolean.ais_remote_access", "on")
        return
    if access == "on":
        text = "Aktywuje " + text
    else:
        text = "Zatrzymuje " + text

    await hass.services.async_call("ais_ai_service", "say_it", {"text": text})
    _LOGGER.info(text)

    if access == "on":
        await _run(
            "pm2 restart tunnel || pm2 start {}"
            " --name tunnel --output /dev/null --error /dev/null"
            " --restart-delay=150000 -- -h http://paczka.pro -p 8180 -s {}".
            format(G_LT_PATH, gate_id))
    else:
        await _run("pm2 delete tunnel && pm2 save")
예제 #2
0
    async def async_step_init(
            self,
            user_input: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
        """Handle the step of the form."""
        errors = {}

        if user_input is not None:
            try:
                cast(AisCloudAuthProvider,
                     self._auth_provider).async_validate_login(
                         user_input["username"],
                         user_input["password"],
                         user_input["gate_id"],
                     )
            except InvalidAuthError:
                errors["base"] = "invalid_auth"

            if not errors:
                user_input.pop("password")
                return await self.async_finish(user_input)

        sercure_android_id_dom = ais_global.get_sercure_android_id_dom()

        return self.async_show_form(
            step_id="init",
            data_schema=vol.Schema({
                vol.Required("username"):
                str,
                vol.Required("password"):
                str,
                vol.Required("gate_id"):
                vol.In([sercure_android_id_dom]),
            }),
            errors=errors,
        )
예제 #3
0
def async_generate_url(hass, webhook_id):
    """Generate the full URL for a webhook_id."""
    "ais-dom gate_id fix"
    from homeassistant.components.ais_dom import ais_global

    gate_id = ais_global.get_sercure_android_id_dom()
    return "{}{}".format("https://" + gate_id + ".paczka.pro",
                         async_generate_path(webhook_id))
예제 #4
0
async def _set_ais_secure_android_id_dom(hass, call):
    # the G_AIS_SECURE_ANDROID_ID_DOM is set only in one place ais_global
    hass.states.async_set(
        "sensor.ais_secure_android_id_dom",
        ais_global.get_sercure_android_id_dom(),
        {
            "friendly_name": "Unikalny identyfikator bramki",
            "icon": "mdi:account-card-details",
        },
    )
예제 #5
0
    def _refresh_(self):
        import pyqrcode
        import png

        gate_id = ais_global.get_sercure_android_id_dom()
        _template = "https://" + gate_id + ".paczka.pro"
        qr_code = pyqrcode.create(_template)
        self._image.truncate(0)
        self._image.seek(0)

        qr_code.png(self._image,
                    scale=6,
                    module_color=[0, 0, 0],
                    background=[0xFF, 0xFF, 0xFF])
예제 #6
0
    async def async_step_user(self, user_input=None):
        """Handle a user initiated set up flow to create OwnTracks webhook."""
        if self._async_current_entries():
            return self.async_abort(reason="one_instance_allowed")

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

        webhook_id, webhook_url, cloudhook = await self._get_webhook_id()
        #
        from homeassistant.components.ais_dom import ais_global

        gate_id = ais_global.get_sercure_android_id_dom()
        webhook_url = webhook_url.replace("localhost:8180",
                                          gate_id + ".paczka.pro")

        secret = generate_secret(16)

        if supports_encryption():
            secret_desc = (
                "The encryption key is {} "
                "(on Android under preferences -> advanced)".format(secret))
        else:
            secret_desc = (
                "Encryption is not supported because libsodium is not "
                "installed.")

        return self.async_create_entry(
            title="OwnTracks",
            data={
                CONF_WEBHOOK_ID: webhook_id,
                CONF_SECRET: secret,
                CONF_CLOUDHOOK: cloudhook,
            },
            description_placeholders={
                "secret":
                "",
                "webhook_url":
                webhook_url,
                "android_url":
                "https://play.google.com/store/apps/details?"
                "id=org.owntracks.android",
                "ios_url":
                "https://itunes.apple.com/us/app/owntracks/id692424691?mt=8",
                "docs_url":
                "https://sviete.github.io/AIS-docs/docs/en/next/ais_bramka_presence_detection.html",
            },
        )
예제 #7
0
 async def async_step_oauth(self, user_input=None):
     request = current_request.get()
     url_host = yarl.URL(request.url).host
     """Handle flow external step."""
     ais_dom = ais_cloud.AisCloudWS(self.hass)
     json_ws_resp = ais_dom.key("google_calendar_web_client_id")
     client_id = json_ws_resp["key"]
     gate_id = ais_global.get_sercure_android_id_dom()
     auth_url = (
         "https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?client_id="
         + client_id + "&redirect_uri=https://" + ais_global.AIS_HOST +
         "/ords/dom/auth/google_calendar_callback" +
         "&response_type=code&scope=https://www.googleapis.com/auth/calendar"
         + "&access_type=offline" + "&state=" + gate_id + "ais0dom" +
         url_host + "ais0domgoogle-calendar-" + self.flow_id)
     return self.async_external_step(step_id="obtain_token", url=auth_url)
예제 #8
0
def _get_external_url(
    hass: HomeAssistant,
    *,
    allow_cloud: bool = True,
    allow_ip: bool = True,
    prefer_cloud: bool = False,
    require_current_request: bool = False,
    require_ssl: bool = False,
    require_standard_port: bool = False,
) -> str:
    """Get external URL of this instance."""
    if prefer_cloud and allow_cloud:
        with suppress(NoURLAvailableError):
            return _get_cloud_url(hass)

    if hass.config.external_url:
        external_url = yarl.URL(hass.config.external_url)
        if ((allow_ip or not is_ip_address(str(external_url.host)))
                and (not require_current_request
                     or external_url.host == _get_request_host()) and
            (not require_standard_port or external_url.is_default_port())
                and (not require_ssl or
                     (external_url.scheme == "https"
                      and not is_ip_address(str(external_url.host))))):
            return normalize_url(str(external_url))

    if allow_cloud:
        with suppress(NoURLAvailableError):
            return _get_cloud_url(
                hass, require_current_request=require_current_request)

    # get ais url
    remote_access = hass.states.get("input_boolean.ais_remote_access").state
    if remote_access == "on":
        import homeassistant.components.ais_dom.ais_global as ais_global

        return "https://" + ais_global.get_sercure_android_id_dom(
        ) + ".paczka.pro"
    else:
        return "http://" + hass.config.api.local_ip + ":" + str(
            hass.config.api.port)

    raise NoURLAvailableError
예제 #9
0
    async def async_step_oauth(self, user_input=None):

        if user_input is not None:
            self.hass.http.register_view(AuthorizationCallbackView)
            request = current_request.get()
            url_host = yarl.URL(request.url).host

            """Handle flow external step."""
            ais_dom = ais_cloud.AisCloudWS(self.hass)
            json_ws_resp = ais_dom.key("supla_mqtt_prod_client_id")
            self.client_id = json_ws_resp["key"]
            gate_id = ais_global.get_sercure_android_id_dom()
            redirect_uri = REDIRECT_URL.replace("AIS_HOST", ais_global.AIS_HOST)
            auth_url = (
                f"{OAUTH_URL}?client_id={self.client_id}&redirect_uri={redirect_uri}&scope={AUTH_SCOPE}&response_type"
                f"=code&state={gate_id}ais0dom{url_host}ais0domsupla-mqtt-{self.flow_id}"
            )
            return self.async_external_step(step_id="obtain_token", url=auth_url)
        return self.async_show_form(step_id="oauth")
예제 #10
0
    async def async_handle_core_service(call):
        """Service handler for handling core services."""
        # ais dom
        import homeassistant.components.ais_dom.ais_global as ais_global

        # return on demo
        if ais_global.get_sercure_android_id_dom() in (
                "dom-274973439829002",
                "dom-demo",
        ):
            return
        ais_command = None
        if "ais_command" in call.data:
            ais_command = call.data["ais_command"]

        if call.service == SERVICE_HOMEASSISTANT_STOP:
            hass.async_create_task(hass.async_stop(ais_command=ais_command))
            return

        try:
            errors = await conf_util.async_check_ha_config_file(hass)
        except HomeAssistantError:
            return

        if errors:
            _LOGGER.error(errors)
            hass.components.persistent_notification.async_create(
                "Config error. See [the logs](/config/logs) for details.",
                "Config validating",
                f"{ha.DOMAIN}.check_config",
            )
            return

        if call.service == SERVICE_HOMEASSISTANT_RESTART:
            hass.async_create_task(
                hass.async_stop(RESTART_EXIT_CODE, ais_command=ais_command))
예제 #11
0
def setup(hass, config):
    """Set up Zeroconf and make AIS dom discoverable."""
    if not ais_global.has_root():
        return True
    from zeroconf import Zeroconf, ServiceInfo

    zero_config = Zeroconf()
    host_ip = util.get_local_ip()

    try:
        return_value = subprocess.check_output(
            "getprop net.hostname",
            timeout=15,
            shell=True,  # nosec
        )
        host_name = return_value.strip().decode("utf-8")
    except subprocess.CalledProcessError:
        host_name = socket.gethostname()
    if len(host_name) == 0:
        # get the mac address
        import uuid

        host_name = "".join([
            "{:02x}".format((uuid.getnode() >> i) & 0xFF)
            for i in range(0, 8 * 6, 8)
        ][::-1])
    if host_name.endswith(".local"):
        host_name = host_name[:-len(".local")]

    hass.states.async_set(
        "sensor.local_host_name",
        host_name.upper(),
        {
            "friendly_name": "Lokalna nazwa hosta",
            "icon": "mdi:dns"
        },
    )
    try:
        host_ip_pton = socket.inet_pton(socket.AF_INET, host_ip)
    except OSError:
        host_ip_pton = socket.inet_pton(socket.AF_INET6, host_ip)
    try:
        gate_id = ais_global.get_sercure_android_id_dom()
    except Exception:
        gate_id = "xxx"

    params = {
        "location_name": hass.config.location_name,
        "version": __version__,
        "company_url": "https://www.ai-speaker.com",
        "gate_id": gate_id,
    }

    # HTTP
    http_info = ServiceInfo(
        "_http._tcp.local.",
        name=host_name + "._http._tcp.local.",
        server=f"{host_name}.local.",
        addresses=[host_ip_pton],
        port=80,
        properties=params,
    )

    # FTP
    ftp_info = ServiceInfo(
        "_ftp._tcp.local.",
        name=host_name + "._ftp._tcp.local.",
        server=f"{host_name}.local.",
        addresses=[host_ip_pton],
        port=21,
        properties=params,
    )

    def zeroconf_hass_start(_event):
        """Expose Home Assistant on zeroconf when it starts.

        Wait till started or otherwise HTTP is not up and running.
        """
        _LOGGER.info("Starting Zeroconf broadcast")
        try:
            zero_config.register_service(http_info)
            zero_config.register_service(ftp_info)
        except NonUniqueNameException:
            _LOGGER.error(
                "Home Assistant instance with identical name present in the local network"
            )

    hass.bus.listen_once(EVENT_HOMEASSISTANT_START, zeroconf_hass_start)

    def stop_zeroconf(event):
        """Stop Zeroconf."""
        zero_config.unregister_service(http_info)
        zero_config.unregister_service(ftp_info)
        zero_config.close()

    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf)

    return True
예제 #12
0
def setup(hass, config):
    """Set up Zeroconf and make AIS dom discoverable."""
    from zeroconf import Zeroconf, ServiceInfo

    zero_config = Zeroconf()
    host_ip = util.get_local_ip()

    try:
        return_value = subprocess.check_output("getprop net.hostname",
                                               shell=True,
                                               timeout=15)
        host_name = return_value.strip().decode("utf-8")
    except subprocess.CalledProcessError:
        host_name = socket.gethostname()
    if len(host_name) == 0:
        # get the mac address
        import uuid

        host_name = "".join([
            "{:02x}".format((uuid.getnode() >> i) & 0xFF)
            for i in range(0, 8 * 6, 8)
        ][::-1])
    if host_name.endswith(".local"):
        host_name = host_name[:-len(".local")]

    hass.states.async_set(
        "sensor.local_host_name",
        host_name.upper(),
        {
            "friendly_name": "Lokalna nazwa hosta",
            "icon": "mdi:dns"
        },
    )
    try:
        ip = socket.inet_pton(socket.AF_INET, host_ip)
    except socket.error:
        ip = socket.inet_pton(socket.AF_INET6, host_ip)
    try:
        gate_id = ais_global.get_sercure_android_id_dom()
    except:
        gate_id = "xxx"

    params = {
        "version": __version__,
        "company_url": "https://www.ai-speaker.com",
        "gate_id": gate_id,
    }

    # HASS
    hass_info = ServiceInfo(
        "_home-assistant._tcp.local.",
        host_name + "._home-assistant._tcp.local.",
        ip,
        8180,
        0,
        0,
        params,
        host_name + ".local.",
    )

    zero_config.register_service(hass_info)

    # HTTP
    http_info = ServiceInfo(
        "_http._tcp.local.",
        host_name + "._http._tcp.local.",
        ip,
        8180,
        0,
        0,
        params,
        host_name + ".local.",
    )

    zero_config.register_service(http_info)

    # MQTT is moved to the android
    # mqtt_info = ServiceInfo("_mqtt._tcp.local.",
    #                         host_name + "._mqtt._tcp.local.",
    #                         ip, 1883, 0, 0,
    #                         params, host_name + ".local.")
    #
    # zero_config.register_service(mqtt_info)

    # FTP
    ftp_info = ServiceInfo(
        "_ftp._tcp.local.",
        host_name + "._ftp._tcp.local.",
        ip,
        1024,
        0,
        0,
        params,
        host_name + ".local.",
    )

    zero_config.register_service(ftp_info)

    def stop_zeroconf(event):
        """Stop Zeroconf."""
        zero_config.unregister_service(hass_info)
        zero_config.unregister_service(http_info)
        # zero_config.unregister_service(mqtt_info)
        zero_config.unregister_service(ftp_info)
        zero_config.close()

    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf)

    return True
예제 #13
0
async def async_setup(hass, config):
    """Set up the Spotify platform."""
    global aisCloud
    aisCloud = ais_cloud.AisCloudWS(hass)
    import json

    import spotipy.oauth2

    global AIS_SPOTIFY_TOKEN

    # info about discovery
    async def do_the_spotify_disco(service):
        """ Called when a Spotify integration has been discovered. """
        await hass.config_entries.flow.async_init(
            "ais_spotify_service", context={"source": "discovery"}, data={})
        await hass.async_block_till_done()

    try:
        json_ws_resp = await aisCloud.async_key("spotify_oauth")
        spotify_redirect_url = json_ws_resp["SPOTIFY_REDIRECT_URL"]
        spotify_client_id = json_ws_resp["SPOTIFY_CLIENT_ID"]
        spotify_client_secret = json_ws_resp["SPOTIFY_CLIENT_SECRET"]
        if "SPOTIFY_SCOPE_FULL" in json_ws_resp:
            spotify_scope = json_ws_resp["SPOTIFY_SCOPE_FULL"]
        else:
            spotify_scope = json_ws_resp["SPOTIFY_SCOPE"]

        try:
            json_ws_resp = await aisCloud.async_key("spotify_token")
            key = json_ws_resp["key"]
            AIS_SPOTIFY_TOKEN = json.loads(key)
        except:
            AIS_SPOTIFY_TOKEN = None
            _LOGGER.info("No AIS_SPOTIFY_TOKEN")
    except Exception as e:
        _LOGGER.error("No spotify oauth info: " + str(e))
        return True

    cache = hass.config.path(DEFAULT_CACHE_PATH)
    gate_id = ais_global.get_sercure_android_id_dom()

    j_state = json.dumps({
        "gate_id": gate_id,
        "real_ip": "real_ip_place",
        "flow_id": "flow_id_place"
    })
    oauth = spotipy.oauth2.SpotifyOAuth(
        spotify_client_id,
        spotify_client_secret,
        spotify_redirect_url,
        scope=spotify_scope,
        cache_path=cache,
        state=j_state,
    )

    setUrl(oauth.get_authorize_url())
    token_info = oauth.get_cached_token()
    if not token_info:
        _LOGGER.info("no spotify token in cache;")
        if AIS_SPOTIFY_TOKEN is not None:
            with open(cache, "w") as outfile:
                json.dump(AIS_SPOTIFY_TOKEN, outfile)
            token_info = oauth.get_cached_token()

    # register services
    if not token_info:
        _LOGGER.info("no spotify token exit")
        hass.async_add_job(do_the_spotify_disco(hass))
        return True

    data = hass.data[DOMAIN] = SpotifyData(hass, oauth)

    async def async_search(call):
        _LOGGER.info("search " + str(call))
        await data.async_process_search(call)

    async def async_get_favorites(call):
        await data.async_process_get_favorites(call)

    def select_search_uri(call):
        _LOGGER.info("select_search_uri")
        data.select_search_uri(call)

    def select_track_uri(call):
        _LOGGER.info("select_track_uri")
        data.select_track_uri(call)

    def change_play_queue(call):
        _LOGGER.info("change_play_queue")
        data.change_play_queue(call)

    hass.services.async_register(DOMAIN, "search", async_search)
    hass.services.async_register(DOMAIN, "get_favorites", async_get_favorites)
    hass.services.async_register(DOMAIN, "select_search_uri",
                                 select_search_uri)
    hass.services.async_register(DOMAIN, "select_track_uri", select_track_uri)
    hass.services.async_register(DOMAIN, "change_play_queue",
                                 change_play_queue)

    return True
예제 #14
0
async def _change_remote_access(hass, call):
    text = " zdalny dostęp do bramki z Internetu"
    access = hass.states.get("input_boolean.ais_remote_access").state
    # not allow to off on demo
    gate_id = ais_global.get_sercure_android_id_dom()
    if (ais_global.get_sercure_android_id_dom() in ("dom-demo", "dom-dev")
            and access != "on"):
        await hass.services.async_call(
            "ais_ai_service",
            "say_it",
            {
                "text":
                "Nie można zatrzymać zdalnego dostępu na instancji demo."
            },
        )
        hass.states.async_set("input_boolean.ais_remote_access", "on")
        return
    if access == "on":
        text = "Aktywuje " + text
    else:
        text = "Zatrzymuje " + text

    await hass.services.async_call("ais_ai_service", "say_it", {"text": text})
    _LOGGER.info(text)

    if access == "on":
        # delete old tunnel
        await _run("pm2 delete tunnel")

        if not os.path.isfile(
                "/data/data/pl.sviete.dom/files/home/.cloudflared/config.yaml"
        ):
            await _run(
                "mkdir -p /data/data/pl.sviete.dom/files/home/.cloudflared")
            with async_timeout.timeout(20):
                web_session = aiohttp_client.async_get_clientsession(hass)
                # store file
                async with web_session.get(
                    "https://ai-speaker.com/ota/ais_cloudflared") as resp:
                    if resp.status == 200:
                        body = await resp.read()
                        f = open(
                            "/data/data/pl.sviete.dom/files/home/.cloudflared/cert.pem",
                            mode="wb",
                        )
                        f.write(body)
                        f.close()
            # create named tunnel
            await _run(
                "/data/data/pl.sviete.dom/files/usr/bin/cloudflared --origincert "
                "/data/data/pl.sviete.dom/files/home/.cloudflared/cert.pem tunnel delete -f "
                + gate_id)
            await _run(
                "/data/data/pl.sviete.dom/files/usr/bin/cloudflared --origincert "
                "/data/data/pl.sviete.dom/files/home/.cloudflared/cert.pem tunnel create "
                + gate_id)
            # rename credentials file
            await _run(
                "mv /data/data/pl.sviete.dom/files/home/.cloudflared/*.json "
                "/data/data/pl.sviete.dom/files/home/.cloudflared/key.json")

            # create config.yaml
            f = open(
                "/data/data/pl.sviete.dom/files/home/.cloudflared/config.yaml",
                mode="w")
            f.write("tunnel: " + gate_id + "\n")
            f.write(
                "credentials-file: /data/data/pl.sviete.dom/files/home/.cloudflared/key.json\n"
            )
            f.write("ingress:\n")
            f.write("  - hostname: " + gate_id + ".paczka.pro\n")
            f.write("    service: http://localhost:8180\n")
            f.write("  - service: http_status:404")
            f.close()

            # route traffic
            await _run(
                "/data/data/pl.sviete.dom/files/usr/bin/cloudflared --origincert "
                "/data/data/pl.sviete.dom/files/home/.cloudflared/cert.pem tunnel route dns -f "
                + gate_id + " " + gate_id + ".paczka.pro")

            # delete cert file
            await _run(
                "rm /data/data/pl.sviete.dom/files/home/.cloudflared/cert.pem")

            #
        await _run(
            "pm2 start /data/data/pl.sviete.dom/files/usr/bin/cloudflared "
            "--name tunnel "
            "--restart-delay=150000 -- --config /data/data/pl.sviete.dom/files/home/.cloudflared/config.yaml "
            "tunnel run")
        await _run("pm2 save")

        # OLD WAY
        # await _run(
        #     "pm2 restart tunnel || pm2 start /data/data/pl.sviete.dom/files/usr/bin/cloudflared"
        #     " --name tunnel --output /dev/null --error /dev/null"
        #     " --restart-delay=150000 -- --hostname http://{}.paczka.pro --url http://localhost:8180".format(
        #         gate_id
        #     )
        # )
    else:
        await _run("pm2 delete tunnel && pm2 save")
        await _run("rm /data/data/pl.sviete.dom/files/home/.cloudflared/*")
예제 #15
0
    async def get(self, request):
        global G_SPOTIFY_AUTH_URL

        hass = request.app["hass"]
        flow_id = request.query["flow_id"]

        try:
            step_ip = request.query["step_ip"]
        except Exception:
            step_ip = 0

        # add the REAL_IP and FLOW_ID to Spotify Auth URL and redirect to Spotify for authentication
        if step_ip == "1":
            real_ip = request.url.host
            #
            if G_SPOTIFY_AUTH_URL is None:
                try:
                    import json

                    import spotipy.oauth2

                    from homeassistant.components import ais_cloud
                    from homeassistant.components.ais_dom import ais_global

                    from . import DEFAULT_CACHE_PATH

                    aisCloud = ais_cloud.AisCloudWS(hass)
                    json_ws_resp = await aisCloud.async_key("spotify_oauth")
                    spotify_redirect_url = json_ws_resp["SPOTIFY_REDIRECT_URL"]
                    spotify_client_id = json_ws_resp["SPOTIFY_CLIENT_ID"]
                    spotify_client_secret = json_ws_resp["SPOTIFY_CLIENT_SECRET"]
                    if "SPOTIFY_SCOPE_FULL" in json_ws_resp:
                        spotify_scope = json_ws_resp["SPOTIFY_SCOPE_FULL"]
                    else:
                        spotify_scope = json_ws_resp["SPOTIFY_SCOPE"]
                except Exception as e:
                    _LOGGER.error("No spotify oauth info: " + str(e))
                    return True

                cache = hass.config.path(DEFAULT_CACHE_PATH)
                gate_id = ais_global.get_sercure_android_id_dom()

                j_state = json.dumps(
                    {
                        "gate_id": gate_id,
                        "real_ip": "real_ip_place",
                        "flow_id": "flow_id_place",
                    }
                )
                oauth = spotipy.oauth2.SpotifyOAuth(
                    spotify_client_id,
                    spotify_client_secret,
                    spotify_redirect_url,
                    scope=spotify_scope,
                    cache_path=cache,
                    state=j_state,
                )

                setUrl(oauth.get_authorize_url())
            G_SPOTIFY_AUTH_URL = G_SPOTIFY_AUTH_URL.replace("real_ip_place", real_ip)
            G_SPOTIFY_AUTH_URL = G_SPOTIFY_AUTH_URL.replace("flow_id_place", flow_id)
            js_text = (
                "<script>window.location.href='" + G_SPOTIFY_AUTH_URL + "'</script>"
            )
            return aiohttp.web_response.Response(
                headers={"content-type": "text/html"}, text=js_text
            )

        # the call was from ais-dom finish the integration
        hass.async_create_task(
            hass.config_entries.flow.async_configure(flow_id=flow_id, user_input="ok")
        )
        # js_text =  "<script>window.close()</script>"
        js_text = (
            "<script>window.location.href='/config/integrations/dashboard'</script>"
        )
        return aiohttp.web_response.Response(
            headers={"content-type": "text/html"}, text=js_text
        )
예제 #16
0
    def play_media(self, media_type, media_content_id, **kwargs):
        """Send the media player the command for playing a media."""
        if media_type == "ais_content_info":
            j_info = json.loads(media_content_id)
            # set image and name and id on list to be able to set correct bookmark and favorites
            ais_global.G_CURR_MEDIA_CONTENT = j_info
            # play
            self._media_content_id = j_info["media_content_id"]
            self._media_status_received_time = dt_util.utcnow()
            if "media_position_ms" in j_info:
                self._media_position = j_info["media_position_ms"]
            else:
                self._media_position = 0
            if "IMAGE_URL" not in j_info:
                self._stream_image = "/static/icons/tile-win-310x150.png"
            else:
                self._stream_image = j_info["IMAGE_URL"]
            self._media_title = j_info["NAME"]
            if j_info["MEDIA_SOURCE"] == ais_global.G_AN_GOOGLE_ASSISTANT:
                # do no change self._media_source
                self._assistant_audio = True
            else:
                self._assistant_audio = False
                self._media_source = j_info["MEDIA_SOURCE"]
            self._currentplaylist = j_info["MEDIA_SOURCE"]
            if "ALBUM_NAME" in j_info:
                self._album_name = j_info["ALBUM_NAME"]
            else:
                self._album_name = ""
            if "DURATION" in j_info:
                self._duration = j_info["DURATION"]

            j_media_info = {
                "media_title": self._media_title,
                "media_source": self._media_source,
                "media_stream_image": self._stream_image,
                "media_album_name": self._album_name,
                "media_content_id": self._media_content_id,
                "setPlayerShuffle": self._shuffle,
                "setMediaPosition": self._media_position,
            }

            _publish_command_to_frame(self.hass, self._device_ip,
                                      "playAudioFullInfo", j_media_info)

            self._playing = True
            self._status = 3

            # go to media player context on localhost
            if self._device_ip == "localhost":
                # refresh state
                old_state = self.hass.states.get(
                    "media_player.wbudowany_glosnik")
                self.hass.states.set(
                    "media_player.wbudowany_glosnik",
                    STATE_PLAYING,
                    old_state.attributes,
                    force_update=True,
                )
                self.hass.services.call(
                    "ais_ai_service",
                    "process_command_from_frame",
                    {
                        "topic": "ais/go_to_player",
                        "payload": ""
                    },
                )

        elif media_type == "ais_info":
            # TODO remove this - it is only used in one case - for local media_extractor
            # set only the audio info
            j_info = json.loads(media_content_id)
            ais_global.G_CURR_MEDIA_CONTENT = j_info
            if "IMAGE_URL" not in j_info:
                self._stream_image = "/static/icons/tile-win-310x150.png"
            else:
                self._stream_image = j_info["IMAGE_URL"]
            self._media_title = j_info["NAME"]
            self._media_source = j_info["MEDIA_SOURCE"]
            self._currentplaylist = j_info["MEDIA_SOURCE"]
            if "ALBUM_NAME" in j_info:
                self._album_name = j_info["ALBUM_NAME"]
            else:
                self._album_name = None
            if "DURATION" in j_info:
                self._duration = j_info["DURATION"]

            try:
                j_media_info = {
                    "media_title": self._media_title,
                    "media_source": self._media_source,
                    "media_stream_image": self._stream_image,
                    "media_album_name": self._album_name,
                }
                _publish_command_to_frame(self.hass, self._device_ip,
                                          "setAudioInfo", j_media_info)
            except Exception as e:
                _LOGGER.debug("problem to publish setAudioInfo: " + str(e))

            self._playing = True
            self._status = 3
            # go to media player context on localhost
            if self._device_ip == "localhost":
                # refresh state
                old_state = self.hass.states.get(
                    "media_player.wbudowany_glosnik")
                self.hass.states.set(
                    "media_player.wbudowany_glosnik",
                    STATE_PLAYING,
                    old_state.attributes,
                    force_update=True,
                )
                self.hass.services.call(
                    "ais_ai_service",
                    "process_command_from_frame",
                    {
                        "topic": "ais/go_to_player",
                        "payload": ""
                    },
                )

        elif media_type == "exo_info":
            self._media_status_received_time = dt_util.utcnow()
            try:
                message = json.loads(
                    media_content_id.decode("utf8").replace("'", '"'))
            except Exception as e:
                message = json.loads(media_content_id)
            try:
                self._volume_level = message.get("currentVolume", 0) / 100
            except Exception as e:
                _LOGGER.warning("no currentVolume info: " + str(e))
            self._status = message.get("currentStatus", 0)
            self._playing = message.get("playing", False)
            self._media_position = message.get("currentPosition", 0)
            if "duration" in message:
                self._duration = message.get("duration", 0)
            temp_stream_image = message.get("media_stream_image", None)
            if temp_stream_image is not None:
                if temp_stream_image.startswith("spotify:image:"):
                    temp_stream_image = temp_stream_image.replace(
                        "spotify:image:", "")
                    self._stream_image = "https://i.scdn.co/image/" + temp_stream_image
                else:
                    self._stream_image = temp_stream_image
            self._media_title = message.get("currentMedia", "AI-Speaker")
            self._media_source = message.get("media_source",
                                             self._media_source)
            self._album_name = message.get("media_album_name", "AI-Speaker")

            # spotify info
            if self._media_source == ais_global.G_AN_SPOTIFY:
                # shuffle
                self._shuffle = message.get("isShuffling", False)
                # mark track on Spotify list
                state = self.hass.states.get("sensor.spotifylist")
                attr = state.attributes
                for i in range(len(attr)):
                    track = attr.get(i)
                    if track["uri"] == message.get("media_content_id", ""):
                        self.hass.states.async_set("sensor.spotifylist", i,
                                                   attr)
                        break

            if "giveMeNextOne" in message:
                play_next = message.get("giveMeNextOne", False)
                # play next audio only if the current was not from assistant
                if play_next is True and self._assistant_audio is False:
                    # TODO remove bookmark
                    self.hass.async_add_job(
                        self.hass.services.async_call(
                            "media_player",
                            "media_next_track",
                            {"entity_id": "media_player.wbudowany_glosnik"},
                        ))
        else:
            # do not call async from threads - this will it can lead to a deadlock!!!
            # if media_content_id.startswith("ais_"):
            #     from homeassistant.components.ais_exo_player import media_browser
            #
            #     media_content_id = media_browser.get_media_content_id_form_ais(
            #         self.hass, media_content_id
            #     )
            # this is currently used when the media are taken from gallery
            if media_content_id.startswith("ais_"):
                import requests

                if media_content_id.startswith("ais_tunein"):
                    url_to_call = media_content_id.split("/", 3)[3]
                    try:
                        response_text = requests.get(url_to_call,
                                                     timeout=2).text
                        response_text = response_text.split("\n")[0]
                        media_content_id = response_text
                        if response_text.endswith(".pls"):
                            response_text = requests.get(response_text,
                                                         timeout=2).text
                            media_content_id = response_text.split(
                                "\n")[1].replace("File1=", "")
                        if response_text.startswith("mms:"):
                            response_text = requests.get(response_text.replace(
                                "mms:", "http:"),
                                                         timeout=2).text
                            media_content_id = response_text.split(
                                "\n")[1].replace("Ref1=", "")
                    except Exception as e:
                        pass
                elif media_content_id.startswith("ais_spotify"):
                    media_content_id = media_content_id.replace(
                        "ais_spotify/", "")
            self._media_content_id = media_content_id
            self._media_position = 0
            self._media_status_received_time = dt_util.utcnow()
            _publish_command_to_frame(self.hass, self._device_ip, "playAudio",
                                      media_content_id)

        # 0. push media info to ais to share with mobile clients
        j_media_info = {
            "media_title": self._media_title,
            "media_source": self._media_source,
            "media_stream_image": self._stream_image,
            "media_album_name": self._album_name,
            "media_content_id": self._media_content_id,
            "gate_id": ais_global.get_sercure_android_id_dom(),
        }
        self._ais_gate.share_media_full_info(j_media_info)
예제 #17
0
async def ais_tunein_library(hass, media_content_id) -> BrowseMedia:
    import xml.etree.ElementTree as ET

    web_session = aiohttp_client.async_get_clientsession(hass)
    if media_content_id == "ais_tunein":
        try:
            #  7 sec should be enough
            with async_timeout.timeout(7):
                # we need this only for demo
                if ais_global.get_sercure_android_id_dom() == "dom-demo":
                    headers = {"accept-language": "pl"}
                    ws_resp = await web_session.get(
                        "http://opml.radiotime.com/", headers=headers)
                else:
                    ws_resp = await web_session.get(
                        "http://opml.radiotime.com/")
                response_text = await ws_resp.text()
                root = ET.fromstring(response_text)  # nosec
                tunein_types = []
                for item in root.findall("body/outline"):
                    tunein_types.append(
                        BrowseMedia(
                            title=item.get("text"),
                            media_class=MEDIA_CLASS_DIRECTORY,
                            media_content_id=media_content_id + "/2/" +
                            item.get("text") + "/" + item.get("URL"),
                            media_content_type=MEDIA_TYPE_APP,
                            can_play=False,
                            can_expand=True,
                            thumbnail="",
                        ))

                root = BrowseMedia(
                    title="TuneIn",
                    media_class=MEDIA_CLASS_DIRECTORY,
                    media_content_id=media_content_id,
                    media_content_type=MEDIA_TYPE_APP,
                    can_expand=True,
                    can_play=False,
                    children=tunein_types,
                )
                return root

        except Exception as e:
            _LOGGER.error("Can't connect tune in api: " + str(e))
            raise BrowseError("Can't connect tune in api: " + str(e))
    elif media_content_id.startswith("ais_tunein/2/"):
        try:
            #  7 sec should be enough
            with async_timeout.timeout(7):
                url_to_call = media_content_id.split("/", 3)[3]
                # we need to set language only for demo
                if ais_global.get_sercure_android_id_dom() == "dom-demo":
                    headers = {"accept-language": "pl"}
                    ws_resp = await web_session.get(url_to_call,
                                                    headers=headers)
                else:
                    ws_resp = await web_session.get(url_to_call)
                response_text = await ws_resp.text()
                root = ET.fromstring(response_text)  # nosec
                tunein_items = []
                for item in root.findall("body/outline"):
                    if item.get("type") == "audio":
                        tunein_items.append(
                            BrowseMedia(
                                title=item.get("text"),
                                media_class=MEDIA_CLASS_DIRECTORY,
                                media_content_id="ais_tunein/2/" +
                                item.get("text") + "/" + item.get("URL"),
                                media_content_type=MEDIA_TYPE_APP,
                                can_play=True,
                                can_expand=False,
                                thumbnail=item.get("image"),
                            ))
                    elif item.get("type") == "link":
                        tunein_items.append(
                            BrowseMedia(
                                title=item.get("text"),
                                media_class=MEDIA_CLASS_DIRECTORY,
                                media_content_id="ais_tunein/2/" +
                                item.get("text") + "/" + item.get("URL"),
                                media_content_type=MEDIA_TYPE_APP,
                                can_play=False,
                                can_expand=True,
                            ))
                for item in root.findall("body/outline/outline"):
                    if item.get("type") == "audio":
                        tunein_items.append(
                            BrowseMedia(
                                title=item.get("text"),
                                media_class=MEDIA_CLASS_DIRECTORY,
                                media_content_id="ais_tunein/2/" +
                                item.get("text") + "/" + item.get("URL"),
                                media_content_type=MEDIA_TYPE_APP,
                                can_play=True,
                                can_expand=False,
                                thumbnail=item.get("image"),
                            ))
                    elif item.get("type") == "link":
                        tunein_items.append(
                            BrowseMedia(
                                title=item.get("text"),
                                media_class=MEDIA_CLASS_DIRECTORY,
                                media_content_id="ais_tunein/2/" +
                                item.get("text") + "/" + item.get("URL"),
                                media_content_type=MEDIA_TYPE_APP,
                                can_play=False,
                                can_expand=True,
                            ))

                root = BrowseMedia(
                    title=media_content_id.split("/", 3)[2],
                    media_class=MEDIA_CLASS_DIRECTORY,
                    media_content_id=media_content_id,
                    media_content_type=MEDIA_TYPE_APP,
                    can_expand=True,
                    can_play=False,
                    children=tunein_items,
                )
                return root

        except Exception as e:
            _LOGGER.error("Can't connect tune in api: " + str(e))
            raise BrowseError("Can't connect tune in api: " + str(e))
예제 #18
0
 def state(self):
     gate_id = ais_global.get_sercure_android_id_dom()
     return "https://" + gate_id + ".paczka.pro"