--- /dev/null
+"""Alexa player provider support for Music Assistant."""
+
+from __future__ import annotations
+
+import asyncio
+import logging
+import os
+import time
+from typing import TYPE_CHECKING, Any
+
+import aiohttp
+from aiohttp import web
+from alexapy import AlexaAPI, AlexaLogin, AlexaProxy
+from music_assistant_models.config_entries import ConfigEntry
+from music_assistant_models.enums import (
+ ConfigEntryType,
+ PlayerFeature,
+ PlayerState,
+ PlayerType,
+ ProviderFeature,
+)
+from music_assistant_models.errors import LoginFailed
+from music_assistant_models.player import DeviceInfo, Player, PlayerMedia
+
+from music_assistant.constants import (
+ CONF_ENTRY_CROSSFADE,
+ CONF_ENTRY_CROSSFADE_DURATION,
+ CONF_ENTRY_FLOW_MODE_ENFORCED,
+ CONF_ENTRY_HTTP_PROFILE,
+ CONF_PASSWORD,
+ CONF_USERNAME,
+)
+from music_assistant.helpers.auth import AuthenticationHelper
+from music_assistant.models.player_provider import PlayerProvider
+
+_LOGGER = logging.getLogger(__name__)
+
+if TYPE_CHECKING:
+ from music_assistant_models.config_entries import (
+ ConfigValueType,
+ ProviderConfig,
+ )
+ from music_assistant_models.provider import ProviderManifest
+
+ from music_assistant.mass import MusicAssistant
+ from music_assistant.models import ProviderInstanceType
+
+CONF_URL = "url"
+CONF_ACTION_AUTH = "auth"
+CONF_AUTH_TOKEN = "token"
+
+SUPPORTED_FEATURES: set[ProviderFeature] = set()
+
+
+async def setup(
+ mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
+) -> ProviderInstanceType:
+ """Initialize provider(instance) with given configuration."""
+ return AlexaProvider(mass, manifest, config)
+
+
+async def get_config_entries(
+ mass: MusicAssistant,
+ instance_id: str | None = None,
+ action: str | None = None,
+ values: dict[str, ConfigValueType] | None = None,
+) -> tuple[ConfigEntry, ...]:
+ """
+ Return Config entries to setup this provider.
+
+ instance_id: id of an existing provider instance (None if new instance setup).
+ action: [optional] action key called from config entries UI.
+ values: the (intermediate) raw values for config entries sent with the action.
+ """
+ # ruff: noqa: ARG001
+ # config flow auth action/step (authenticate button clicked)
+ if action == CONF_ACTION_AUTH and values:
+ async with AuthenticationHelper(mass, str(values["session_id"])) as auth_helper:
+ login = AlexaLogin(
+ url=str(values[CONF_URL]),
+ email=str(values[CONF_USERNAME]),
+ password=str(values[CONF_PASSWORD]),
+ outputpath=lambda x: x,
+ )
+
+ # --- Proxy authentication logic using AlexaProxy ---
+ # Build the proxy path and URL
+ proxy_path = "/alexa/auth/proxy/"
+ post_path = "/alexa/auth/proxy/ap/signin"
+ base_url = mass.webserver.base_url.rstrip("/")
+ proxy_url = f"{base_url}{proxy_path}"
+
+ # Create AlexaProxy instance
+ proxy = AlexaProxy(login, proxy_url)
+
+ # Handler that delegates to AlexaProxy's all_handler
+ async def proxy_handler(request: web.Request) -> Any:
+ response = await proxy.all_handler(request)
+ if "Successfully logged in" in getattr(response, "text", ""):
+ # Notify the callback URL
+ async with aiohttp.ClientSession() as session:
+ await session.get(auth_helper.callback_url)
+ return web.Response(
+ text="""
+ <html>
+ <body>
+ <h2>Login successful!</h2>
+ <p>You may now close this window.</p>
+ </body>
+ </html>
+ """,
+ content_type="text/html",
+ )
+ return response
+
+ # Register GET for the base proxy path
+ mass.webserver.register_dynamic_route(proxy_path, proxy_handler, "GET")
+ # Register POST for the specific signin helper path
+ mass.webserver.register_dynamic_route(post_path, proxy_handler, "POST")
+
+ try:
+ await auth_helper.authenticate(proxy_url, timeout=300)
+ await save_cookie(login, str(values[CONF_USERNAME]), mass)
+ except KeyError:
+ # no URL param was found so user probably cancelled the auth
+ pass
+ except Exception as error:
+ raise LoginFailed(f"Failed to authenticate with Amazon '{error}'.")
+ finally:
+ mass.webserver.unregister_dynamic_route(proxy_path)
+ mass.webserver.unregister_dynamic_route(post_path)
+
+ return (
+ ConfigEntry(
+ key=CONF_URL,
+ type=ConfigEntryType.STRING,
+ label="URL",
+ required=True,
+ default_value="amazon.com",
+ ),
+ ConfigEntry(
+ key=CONF_USERNAME,
+ type=ConfigEntryType.STRING,
+ label="E-Mail",
+ required=True,
+ ),
+ ConfigEntry(
+ key=CONF_PASSWORD,
+ type=ConfigEntryType.SECURE_STRING,
+ label="Password",
+ required=True,
+ ),
+ ConfigEntry(
+ key=CONF_ACTION_AUTH,
+ type=ConfigEntryType.ACTION,
+ label="Authenticate with Amazon",
+ description="Click to start the authentication process.",
+ action=CONF_ACTION_AUTH,
+ depends_on=CONF_URL,
+ ),
+ )
+
+
+async def save_cookie(login: AlexaLogin, username: str, mass: MusicAssistant) -> None:
+ """Save the cookie file for the Alexa login."""
+ if login._session is None:
+ _LOGGER.error("AlexaLogin session is not initialized.")
+ return
+
+ cookie_dir = os.path.join(mass.storage_path, ".alexa")
+ await asyncio.to_thread(os.makedirs, cookie_dir, exist_ok=True)
+ cookie_path = os.path.join(cookie_dir, f"alexa_media.{username}.pickle")
+ login._cookiefile = [login._outputpath(cookie_path)]
+ if (login._cookiefile[0]) and await asyncio.to_thread(os.path.exists, login._cookiefile[0]):
+ _LOGGER.debug("Removing outdated cookiefile %s", login._cookiefile[0])
+ await delete_cookie(login._cookiefile[0])
+ cookie_jar = login._session.cookie_jar
+ assert isinstance(cookie_jar, aiohttp.CookieJar)
+ if login._debug:
+ _LOGGER.debug("Saving cookie to %s", login._cookiefile[0])
+ try:
+ await asyncio.to_thread(cookie_jar.save, login._cookiefile[0])
+ except (OSError, EOFError, TypeError, AttributeError):
+ _LOGGER.debug("Error saving pickled cookie to %s", login._cookiefile[0])
+
+
+async def delete_cookie(cookiefile: str) -> None:
+ """Delete the specified cookie file."""
+ if await asyncio.to_thread(os.path.exists, cookiefile):
+ try:
+ await asyncio.to_thread(os.remove, cookiefile)
+ _LOGGER.debug("Deleted cookie file: %s", cookiefile)
+ except OSError as e:
+ _LOGGER.error("Failed to delete cookie file %s: %s", cookiefile, e)
+ else:
+ _LOGGER.debug("Cookie file %s does not exist, nothing to delete.", cookiefile)
+
+
+class AlexaProvider(PlayerProvider):
+ """Implementation of an Alexa Device Provider."""
+
+ class AlexaDevice:
+ """Representation of an Alexa Device."""
+
+ _device_type: str
+ device_serial_number: str
+ _device_family: str
+ _cluster_members: str
+ _locale: str
+
+ login: AlexaLogin
+ devices: dict[str, AlexaProvider.AlexaDevice]
+
+ @property
+ def supported_features(self) -> set[ProviderFeature]:
+ """Return the features supported by this Provider."""
+ return SUPPORTED_FEATURES
+
+ def __init__(
+ self, mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
+ ) -> None:
+ """Initialize AlexaProvider and its device mapping."""
+ super().__init__(mass, manifest, config)
+ self.devices = {}
+
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
+ self.login = AlexaLogin(
+ url=str(self.config.get_value(CONF_URL)),
+ email=str(self.config.get_value(CONF_USERNAME)),
+ password=str(self.config.get_value(CONF_PASSWORD)),
+ outputpath=lambda x: x,
+ )
+
+ cookie_dir = os.path.join(self.mass.storage_path, ".alexa")
+ await asyncio.to_thread(os.makedirs, cookie_dir, exist_ok=True)
+ cookie_path = os.path.join(
+ cookie_dir, f"alexa_media.{self.config.get_value(CONF_USERNAME)}.pickle"
+ )
+ self.login._cookiefile = [self.login._outputpath(cookie_path)]
+
+ await self.login.login(cookies=await self.login.load_cookie())
+
+ devices = await AlexaAPI.get_devices(self.login)
+
+ if devices is None:
+ return
+
+ for device in devices:
+ if device.get("capabilities") and "MUSIC_SKILL" in device.get("capabilities"):
+ dev_name = device["accountName"]
+ player_id = dev_name
+ player = Player(
+ player_id=player_id,
+ provider=self.instance_id,
+ type=PlayerType.PLAYER,
+ name=player_id,
+ available=True,
+ powered=False,
+ device_info=DeviceInfo(),
+ supported_features={
+ PlayerFeature.VOLUME_SET,
+ PlayerFeature.PAUSE,
+ PlayerFeature.VOLUME_MUTE,
+ },
+ )
+ await self.mass.players.register_or_update(player)
+ # Initialize AlexaDevice and store in self.devices
+ device_object = self.AlexaDevice()
+ device_object._device_type = device["deviceType"]
+ device_object.device_serial_number = device["serialNumber"]
+ device_object._device_family = device["deviceOwnerCustomerId"]
+ device_object._cluster_members = device["clusterMembers"]
+ device_object._locale = "en-US"
+ self.devices[player_id] = device_object
+
+ async def get_player_config_entries(self, player_id: str) -> tuple[ConfigEntry, ...]:
+ """Return all (provider/player specific) Config Entries for the given player (if any)."""
+ base_entries = await super().get_player_config_entries(player_id)
+ return (
+ *base_entries,
+ CONF_ENTRY_FLOW_MODE_ENFORCED,
+ CONF_ENTRY_CROSSFADE,
+ CONF_ENTRY_CROSSFADE_DURATION,
+ CONF_ENTRY_HTTP_PROFILE,
+ )
+
+ async def cmd_stop(self, player_id: str) -> None:
+ """Send STOP command to given player."""
+ if not (player := self.mass.players.get(player_id, raise_unavailable=False)):
+ return
+ device_object = self.devices[player_id]
+ api = AlexaAPI(device_object, self.login)
+ await api.stop()
+
+ player.state = PlayerState.IDLE
+ self.mass.players.update(player_id)
+
+ async def cmd_play(self, player_id: str) -> None:
+ """Send PLAY command to given player."""
+ if not (player := self.mass.players.get(player_id, raise_unavailable=False)):
+ return
+ device_object = self.devices[player_id]
+ api = AlexaAPI(device_object, self.login)
+ await api.play()
+
+ player.state = PlayerState.PLAYING
+ self.mass.players.update(player_id)
+
+ async def cmd_pause(self, player_id: str) -> None:
+ """Send PAUSE command to given player."""
+ if not (player := self.mass.players.get(player_id, raise_unavailable=False)):
+ return
+ device_object = self.devices[player_id]
+ api = AlexaAPI(device_object, self.login)
+ await api.pause()
+
+ player.state = PlayerState.PLAYING
+ self.mass.players.update(player_id)
+
+ async def cmd_volume_set(self, player_id: str, volume_level: int) -> None:
+ """Send VOLUME_SET command to given player."""
+ if not (player := self.mass.players.get(player_id, raise_unavailable=False)):
+ return
+ device_object = self.devices[player_id]
+ api = AlexaAPI(device_object, self.login)
+ await api.set_volume(volume_level / 100)
+
+ player.volume_level = volume_level
+ self.mass.players.update(player_id)
+
+ async def cmd_volume_mute(self, player_id: str, muted: bool) -> None:
+ """Send VOLUME MUTE command to given player."""
+ if not (player := self.mass.players.get(player_id, raise_unavailable=False)):
+ return
+ device_object = self.devices[player_id]
+ api = AlexaAPI(device_object, self.login)
+ await api.set_volume(0)
+
+ player.volume_level = 0
+ self.mass.players.update(player_id)
+
+ async def play_media(
+ self,
+ player_id: str,
+ media: PlayerMedia,
+ ) -> None:
+ """Handle PLAY MEDIA on given player.
+
+ This is called by the Players controller to start playing a mediaitem on the given player.
+ The provider's own implementation should work out how to handle this request.
+
+ - player_id: player_id of the player to handle the command.
+ - media: Details of the item that needs to be played on the player.
+ """
+ if not (player := self.mass.players.get(player_id)):
+ return
+
+ async with aiohttp.ClientSession() as session:
+ try:
+ async with session.post(
+ "http://localhost:3000/ma/push-url",
+ json={"streamUrl": media.uri},
+ timeout=aiohttp.ClientTimeout(total=10),
+ ) as resp:
+ await resp.text()
+ except Exception as exc:
+ _LOGGER.error("Failed to push URL to Alexa: %s", exc)
+ return
+ device_object = self.devices[player_id]
+ api = AlexaAPI(device_object, self.login)
+ await api.run_custom("Ask music assistant to play audio")
+
+ player.current_media = media
+ player.elapsed_time = 0
+ player.elapsed_time_last_updated = time.time()
+ player.state = PlayerState.PLAYING
+ self.mass.players.update(player_id)
--- /dev/null
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="100%" viewBox="0 0 471 320" enable-background="new 0 0 471 320" xml:space="preserve">
+<path fill="#FFFFFF" opacity="1.000000" stroke="none"
+ d="
+M252.000000,321.000000
+ C168.000000,321.000000 84.500000,321.000000 1.000000,321.000000
+ C1.000000,214.333328 1.000000,107.666664 1.000000,1.000000
+ C158.000000,1.000000 315.000000,1.000000 472.000000,1.000000
+ C472.000000,107.666664 472.000000,214.333328 472.000000,321.000000
+ C398.833344,321.000000 325.666656,321.000000 252.000000,321.000000
+M132.674255,194.936462
+ C129.750748,193.196274 126.827248,191.456085 123.903748,189.715897
+ C123.382484,189.910019 122.861221,190.104126 122.339966,190.298248
+ C122.686920,192.168167 122.475426,194.467758 123.508179,195.810028
+ C125.169853,197.969727 127.649689,199.501373 129.789658,201.291443
+ C145.735291,214.629837 163.721878,224.532745 183.479233,230.573761
+ C204.683884,237.057297 226.382736,240.448639 248.857193,238.189590
+ C263.558411,236.711884 277.870178,234.228760 291.890228,229.621231
+ C304.964844,225.324432 317.220428,219.451309 328.600067,211.743301
+ C331.052826,210.081940 333.296844,207.634888 331.611481,204.794159
+ C330.934357,203.652832 327.270905,203.457596 325.325897,204.037216
+ C316.938202,206.536667 308.847961,210.226120 300.345154,212.105835
+ C284.631165,215.579681 268.808594,219.303070 252.837875,220.681107
+ C234.307770,222.280029 215.671768,220.766968 197.237137,217.116592
+ C174.898849,212.693207 153.617844,205.525391 132.674255,194.936462
+M325.009338,136.041565
+ C324.428314,136.336014 323.849152,136.634186 323.266022,136.924377
+ C314.419464,141.326782 310.074066,149.711731 312.166962,158.343582
+ C314.388153,167.504486 320.848175,172.620819 331.448944,171.778214
+ C337.577087,171.291122 343.528687,168.583481 349.587708,166.867294
+ C351.523376,173.037857 356.787018,168.816177 360.996826,170.131409
+ C360.996826,154.372833 361.169189,139.395325 360.923096,124.424690
+ C360.802612,117.096924 356.831726,111.144165 349.677856,110.309685
+ C340.865692,109.281754 331.693237,109.631516 322.880341,110.854744
+ C315.638092,111.859970 315.142944,113.922653 316.656494,122.267784
+ C318.377289,121.868019 320.057373,121.329361 321.778381,121.104927
+ C327.614746,120.343842 333.451202,119.351593 339.315247,119.106102
+ C344.312836,118.896873 348.473724,122.344917 348.916077,126.513443
+ C349.241913,129.584259 348.980011,132.717422 348.980011,136.001831
+ C340.850372,136.001831 333.347015,136.001831 325.009338,136.041565
+M111.407669,156.902222
+ C113.308113,167.283493 119.563362,171.092606 126.897125,171.914474
+ C134.954834,172.817505 142.247879,170.252029 148.759354,165.206711
+ C149.615585,170.461060 152.079178,171.276596 159.999191,169.186676
+ C159.999191,154.105118 160.103333,138.974716 159.945587,123.847038
+ C159.886276,118.158936 156.924438,113.480980 151.952011,111.177841
+ C144.070053,107.527031 135.478607,108.737801 127.144043,109.476624
+ C123.989868,109.756233 120.833679,110.902931 117.859688,112.103470
+ C114.006866,113.658760 114.586754,117.296455 115.410477,120.196518
+ C115.626579,120.957336 119.167160,121.131348 121.132027,120.971756
+ C127.313194,120.469688 133.460831,119.294930 139.641373,119.130875
+ C144.186768,119.010216 147.150742,122.113701 147.882919,126.542610
+ C148.381760,129.560013 147.978317,132.726578 147.978317,135.844315
+ C142.104126,135.541199 136.850601,134.925858 131.617767,135.068741
+ C119.102882,135.410477 110.922241,142.758881 111.407669,156.902222
+M251.000000,127.427681
+ C251.604309,120.637886 248.226318,115.188354 242.945694,112.331398
+ C237.528961,109.400772 231.093674,108.433052 224.501740,108.864578
+ C215.531891,109.451775 206.489487,115.592957 202.885101,123.996735
+ C198.725174,133.695786 199.155960,143.633362 201.362061,153.422791
+ C203.237442,161.744659 208.352798,168.390884 217.072311,170.368820
+ C223.081100,171.731842 229.501801,172.161926 235.646835,171.724045
+ C239.596069,171.442627 243.682693,169.288498 247.089554,167.037979
+ C248.482101,166.118057 248.242203,162.726944 248.744965,160.460068
+ C247.021240,160.298401 245.268677,159.854843 243.579147,160.027603
+ C238.391434,160.557999 233.225220,161.902832 228.055435,161.879379
+ C217.172882,161.830017 210.236038,153.667175 211.470261,143.997528
+ C219.644104,143.997528 227.759171,144.538025 235.770187,143.856110
+ C245.078110,143.063782 250.338928,137.133926 251.000000,127.427681
+M305.511200,162.988770
+ C300.050690,154.921082 294.590179,146.853409 289.119843,138.771194
+ C294.844147,130.908707 300.649048,123.043961 306.320038,115.083817
+ C308.158966,112.502647 306.953491,110.299255 303.731842,110.014679
+ C298.875305,109.585693 294.912476,110.736244 292.156555,115.355179
+ C289.226837,120.265396 285.723419,124.833313 282.143158,130.025421
+ C278.369446,124.532623 275.031464,120.282593 272.379791,115.640541
+ C269.810669,111.143028 266.249359,109.428642 261.365387,110.039963
+ C259.994507,110.211571 257.854187,110.546982 257.541443,111.368584
+ C257.083221,112.572426 257.540710,114.558189 258.325500,115.730316
+ C262.851410,122.489822 267.761292,128.997742 272.131409,135.852066
+ C273.212677,137.547943 273.722137,140.729065 272.849274,142.331100
+ C270.607727,146.445221 267.342285,149.987396 264.672424,153.885773
+ C261.343933,158.745758 258.202179,163.733627 254.868698,168.836304
+ C263.339203,171.609909 266.976593,170.515015 271.129578,164.355042
+ C274.547638,159.285187 277.904968,154.174362 281.541595,148.701889
+ C286.226501,155.396591 290.568115,161.689209 295.036743,167.890320
+ C295.700958,168.812027 296.927673,169.827209 297.955841,169.897537
+ C301.542969,170.142853 305.157166,169.992462 310.169739,169.992462
+ C308.375671,167.193405 307.202850,165.363647 305.511200,162.988770
+M173.000000,95.527779
+ C173.000000,116.348511 172.964066,137.169342 173.015518,157.989929
+ C173.040390,168.052216 182.473022,174.196548 191.951355,170.602356
+ C196.172623,169.001617 195.071198,165.575241 194.772018,162.696594
+ C194.690704,161.914322 192.020706,161.486603 190.627350,160.732544
+ C189.043686,159.875488 186.895966,159.173203 186.192001,157.792526
+ C185.211548,155.869614 185.053406,153.380371 185.045227,151.131683
+ C184.968842,130.144653 185.056732,109.156807 184.894287,88.170761
+ C184.881012,86.453903 183.716324,83.586288 182.504089,83.228821
+ C175.279327,81.098343 173.000595,83.036652 173.000015,90.541359
+ C172.999908,91.873886 173.000000,93.206413 173.000000,95.527779
+M350.008545,208.357910
+ C350.966156,203.602005 352.443237,198.871841 352.680573,194.080261
+ C352.816284,191.341034 352.089111,187.726303 348.019409,187.043686
+ C335.630493,184.965698 323.514404,185.359497 312.190399,191.479614
+ C310.392761,192.451157 309.175446,194.496429 306.774536,196.999527
+ C309.374573,196.999527 310.200500,197.030640 311.023499,196.994690
+ C318.449280,196.670334 325.872894,196.131500 333.300568,196.070267
+ C338.566010,196.026871 341.949707,199.401642 340.853973,203.315613
+ C339.283478,208.925644 337.326141,214.428024 335.507446,219.967651
+ C334.580109,222.792221 333.594910,225.597839 332.635895,228.412033
+ C333.147705,228.870132 333.659515,229.328232 334.171295,229.786346
+ C336.428009,228.286346 339.397125,227.267685 340.800323,225.190994
+ C344.232635,220.111176 346.941315,214.542435 350.008545,208.357910
+z"/>
+<path fill="#05A0D1" opacity="1.000000" stroke="none"
+ d="
+M133.044678,195.005905
+ C153.617844,205.525391 174.898849,212.693207 197.237137,217.116592
+ C215.671768,220.766968 234.307770,222.280029 252.837875,220.681107
+ C268.808594,219.303070 284.631165,215.579681 300.345154,212.105835
+ C308.847961,210.226120 316.938202,206.536667 325.325897,204.037216
+ C327.270905,203.457596 330.934357,203.652832 331.611481,204.794159
+ C333.296844,207.634888 331.052826,210.081940 328.600067,211.743301
+ C317.220428,219.451309 304.964844,225.324432 291.890228,229.621231
+ C277.870178,234.228760 263.558411,236.711884 248.857193,238.189590
+ C226.382736,240.448639 204.683884,237.057297 183.479233,230.573761
+ C163.721878,224.532745 145.735291,214.629837 129.789658,201.291443
+ C127.649689,199.501373 125.169853,197.969727 123.508179,195.810028
+ C122.475426,194.467758 122.686920,192.168167 122.339966,190.298248
+ C122.861221,190.104126 123.382484,189.910019 123.903748,189.715897
+ C126.827248,191.456085 129.750748,193.196274 133.044678,195.005905
+z"/>
+<path fill="#05A0D1" opacity="1.000000" stroke="none"
+ d="
+M325.426483,136.021698
+ C333.347015,136.001831 340.850372,136.001831 348.980011,136.001831
+ C348.980011,132.717422 349.241913,129.584259 348.916077,126.513443
+ C348.473724,122.344917 344.312836,118.896873 339.315247,119.106102
+ C333.451202,119.351593 327.614746,120.343842 321.778381,121.104927
+ C320.057373,121.329361 318.377289,121.868019 316.656494,122.267784
+ C315.142944,113.922653 315.638092,111.859970 322.880341,110.854744
+ C331.693237,109.631516 340.865692,109.281754 349.677856,110.309685
+ C356.831726,111.144165 360.802612,117.096924 360.923096,124.424690
+ C361.169189,139.395325 360.996826,154.372833 360.996826,170.131409
+ C356.787018,168.816177 351.523376,173.037857 349.587708,166.867294
+ C343.528687,168.583481 337.577087,171.291122 331.448944,171.778214
+ C320.848175,172.620819 314.388153,167.504486 312.166962,158.343582
+ C310.074066,149.711731 314.419464,141.326782 323.266022,136.924377
+ C323.849152,136.634186 324.428314,136.336014 325.426483,136.021698
+M349.000000,152.287567
+ C349.000000,149.990402 349.000000,147.693222 349.000000,144.736740
+ C343.874084,144.478485 339.142700,143.918350 334.435883,144.078964
+ C327.512390,144.315216 324.510468,148.156418 325.001404,155.354919
+ C325.308533,159.858597 329.759369,162.917465 335.767059,161.863815
+ C339.503571,161.208511 343.165771,159.554596 346.549957,157.761322
+ C347.819885,157.088364 348.209198,154.753571 349.000000,152.287567
+z"/>
+<path fill="#05A0D1" opacity="1.000000" stroke="none"
+ d="
+M111.235481,156.512482
+ C110.922241,142.758881 119.102882,135.410477 131.617767,135.068741
+ C136.850601,134.925858 142.104126,135.541199 147.978317,135.844315
+ C147.978317,132.726578 148.381760,129.560013 147.882919,126.542610
+ C147.150742,122.113701 144.186768,119.010216 139.641373,119.130875
+ C133.460831,119.294930 127.313194,120.469688 121.132027,120.971756
+ C119.167160,121.131348 115.626579,120.957336 115.410477,120.196518
+ C114.586754,117.296455 114.006866,113.658760 117.859688,112.103470
+ C120.833679,110.902931 123.989868,109.756233 127.144043,109.476624
+ C135.478607,108.737801 144.070053,107.527031 151.952011,111.177841
+ C156.924438,113.480980 159.886276,118.158936 159.945587,123.847038
+ C160.103333,138.974716 159.999191,154.105118 159.999191,169.186676
+ C152.079178,171.276596 149.615585,170.461060 148.759354,165.206711
+ C142.247879,170.252029 134.954834,172.817505 126.897125,171.914474
+ C119.563362,171.092606 113.308113,167.283493 111.235481,156.512482
+M129.304321,161.758820
+ C143.886307,162.864822 148.370697,159.143051 147.922806,146.287720
+ C147.912247,145.984726 147.691788,145.689056 147.287445,144.702347
+ C142.867615,144.479202 138.120926,143.834030 133.431854,144.123199
+ C129.211273,144.383499 125.185081,146.313644 124.211395,150.902023
+ C123.333275,155.040115 123.752449,159.274567 129.304321,161.758820
+z"/>
+<path fill="#05A0D1" opacity="1.000000" stroke="none"
+ d="
+M250.999969,127.867981
+ C250.338928,137.133926 245.078110,143.063782 235.770187,143.856110
+ C227.759171,144.538025 219.644104,143.997528 211.470261,143.997528
+ C210.236038,153.667175 217.172882,161.830017 228.055435,161.879379
+ C233.225220,161.902832 238.391434,160.557999 243.579147,160.027603
+ C245.268677,159.854843 247.021240,160.298401 248.744965,160.460068
+ C248.242203,162.726944 248.482101,166.118057 247.089554,167.037979
+ C243.682693,169.288498 239.596069,171.442627 235.646835,171.724045
+ C229.501801,172.161926 223.081100,171.731842 217.072311,170.368820
+ C208.352798,168.390884 203.237442,161.744659 201.362061,153.422791
+ C199.155960,143.633362 198.725174,133.695786 202.885101,123.996735
+ C206.489487,115.592957 215.531891,109.451775 224.501740,108.864578
+ C231.093674,108.433052 237.528961,109.400772 242.945694,112.331398
+ C248.226318,115.188354 251.604309,120.637886 250.999969,127.867981
+M237.940445,131.154510
+ C240.048218,127.250107 238.941589,123.111465 236.044189,121.020882
+ C233.407089,119.118103 229.154648,118.589249 225.713104,118.835625
+ C216.975342,119.461166 213.316727,125.454582 212.234512,133.571243
+ C213.073364,133.844513 213.969940,134.217499 214.903198,134.427902
+ C222.761597,136.199814 230.621704,137.897842 237.940445,131.154510
+z"/>
+<path fill="#05A0D1" opacity="1.000000" stroke="none"
+ d="
+M305.770630,163.261322
+ C307.202850,165.363647 308.375671,167.193405 310.169739,169.992462
+ C305.157166,169.992462 301.542969,170.142853 297.955841,169.897537
+ C296.927673,169.827209 295.700958,168.812027 295.036743,167.890320
+ C290.568115,161.689209 286.226501,155.396591 281.541595,148.701889
+ C277.904968,154.174362 274.547638,159.285187 271.129578,164.355042
+ C266.976593,170.515015 263.339203,171.609909 254.868698,168.836304
+ C258.202179,163.733627 261.343933,158.745758 264.672424,153.885773
+ C267.342285,149.987396 270.607727,146.445221 272.849274,142.331100
+ C273.722137,140.729065 273.212677,137.547943 272.131409,135.852066
+ C267.761292,128.997742 262.851410,122.489822 258.325500,115.730316
+ C257.540710,114.558189 257.083221,112.572426 257.541443,111.368584
+ C257.854187,110.546982 259.994507,110.211571 261.365387,110.039963
+ C266.249359,109.428642 269.810669,111.143028 272.379791,115.640541
+ C275.031464,120.282593 278.369446,124.532623 282.143158,130.025421
+ C285.723419,124.833313 289.226837,120.265396 292.156555,115.355179
+ C294.912476,110.736244 298.875305,109.585693 303.731842,110.014679
+ C306.953491,110.299255 308.158966,112.502647 306.320038,115.083817
+ C300.649048,123.043961 294.844147,130.908707 289.119843,138.771194
+ C294.590179,146.853409 300.050690,154.921082 305.770630,163.261322
+z"/>
+<path fill="#05A0D1" opacity="1.000000" stroke="none"
+ d="
+M173.000000,95.033356
+ C173.000000,93.206413 172.999908,91.873886 173.000015,90.541359
+ C173.000595,83.036652 175.279327,81.098343 182.504089,83.228821
+ C183.716324,83.586288 184.881012,86.453903 184.894287,88.170761
+ C185.056732,109.156807 184.968842,130.144653 185.045227,151.131683
+ C185.053406,153.380371 185.211548,155.869614 186.192001,157.792526
+ C186.895966,159.173203 189.043686,159.875488 190.627350,160.732544
+ C192.020706,161.486603 194.690704,161.914322 194.772018,162.696594
+ C195.071198,165.575241 196.172623,169.001617 191.951355,170.602356
+ C182.473022,174.196548 173.040390,168.052216 173.015518,157.989929
+ C172.964066,137.169342 173.000000,116.348511 173.000000,95.033356
+z"/>
+<path fill="#05A0D1" opacity="1.000000" stroke="none"
+ d="
+M349.974884,208.764191
+ C346.941315,214.542435 344.232635,220.111176 340.800323,225.190994
+ C339.397125,227.267685 336.428009,228.286346 334.171295,229.786346
+ C333.659515,229.328232 333.147705,228.870132 332.635895,228.412033
+ C333.594910,225.597839 334.580109,222.792221 335.507446,219.967651
+ C337.326141,214.428024 339.283478,208.925644 340.853973,203.315613
+ C341.949707,199.401642 338.566010,196.026871 333.300568,196.070267
+ C325.872894,196.131500 318.449280,196.670334 311.023499,196.994690
+ C310.200500,197.030640 309.374573,196.999527 306.774536,196.999527
+ C309.175446,194.496429 310.392761,192.451157 312.190399,191.479614
+ C323.514404,185.359497 335.630493,184.965698 348.019409,187.043686
+ C352.089111,187.726303 352.816284,191.341034 352.680573,194.080261
+ C352.443237,198.871841 350.966156,203.602005 349.974884,208.764191
+z"/>
+<path fill="#FFFFFF" opacity="1.000000" stroke="none"
+ d="
+M349.000000,152.731995
+ C348.209198,154.753571 347.819885,157.088364 346.549957,157.761322
+ C343.165771,159.554596 339.503571,161.208511 335.767059,161.863815
+ C329.759369,162.917465 325.308533,159.858597 325.001404,155.354919
+ C324.510468,148.156418 327.512390,144.315216 334.435883,144.078964
+ C339.142700,143.918350 343.874084,144.478485 349.000000,144.736740
+ C349.000000,147.693222 349.000000,149.990402 349.000000,152.731995
+z"/>
+<path fill="#FFFFFF" opacity="1.000000" stroke="none"
+ d="
+M128.994263,161.481964
+ C123.752449,159.274567 123.333275,155.040115 124.211395,150.902023
+ C125.185081,146.313644 129.211273,144.383499 133.431854,144.123199
+ C138.120926,143.834030 142.867615,144.479202 147.287445,144.702347
+ C147.691788,145.689056 147.912247,145.984726 147.922806,146.287720
+ C148.370697,159.143051 143.886307,162.864822 128.994263,161.481964
+z"/>
+<path fill="#FFFFFF" opacity="1.000000" stroke="none"
+ d="
+M237.773071,131.513702
+ C230.621704,137.897842 222.761597,136.199814 214.903198,134.427902
+ C213.969940,134.217499 213.073364,133.844513 212.234512,133.571243
+ C213.316727,125.454582 216.975342,119.461166 225.713104,118.835625
+ C229.154648,118.589249 233.407089,119.118103 236.044189,121.020882
+ C238.941589,123.111465 240.048218,127.250107 237.773071,131.513702
+z"/>
+</svg>