Ejemplo n.º 1
0
 def communicate(self, raw: str, wait=True) -> str:
     eof = False
     response = ''
     data = raw.strip() + '\n'
     num_lines = data.count('\n')
     try:
         self._ssl_sock.sendall(data.encode('utf-8'))
         if not wait:
             return None
         while not eof:
             response += self._ssl_sock.recv().decode('utf-8')
             eof = response.count("\n") == num_lines or not response
         return response
     except socket.error as e:
         if 'read operation timed out' in str(e):
             raise Error("Timed out waiting for CLI response. "
                         "Perhaps the tunnel endpoint is incorrect, "
                         "or the LMS CLI is down?")
         else:
             print_d("Couldn't communicate with Squeezebox ({error!r})",
                     error=e)
         self.failures += 1
         if self.failures >= self._MAX_FAILURES:
             self.is_connected = False
             self._ssl_sock.close()
             raise Error("Too many Squeezebox failures. Disconnecting")
         return None
Ejemplo n.º 2
0
 def connected(client, userdata, flags, rc):
     print_d("Connected to {client}. Subscribing to {topic}",
             client=self.client,
             topic=self.resp_topic)
     result, mid = self.client.subscribe(self.resp_topic, qos=1)
     if result != MQTT_ERR_SUCCESS:
         raise Error("Couldn't subscribe to '{topic}'", self.resp_topic)
Ejemplo n.º 3
0
 def playlists(self):
     if not self.__playlists:
         resp = self.__a_request("playlists 0 255", raw=True)
         self.__playlists = [v for k, v in self.__pairs_from(resp)
                             if k == 'playlist']
         print_d(with_example("Loaded %d LMS playlists", self.__playlists))
     return self.__playlists
Ejemplo n.º 4
0
def connect_cli():
    global telnet
    telnet = telnetlib.Telnet(host=MQTT_SETTINGS.internal_server_hostname,
                              port=LMS_SETTINGS.cli_port,
                              timeout=5)
    print_d("Connected to Squeezeserver CLI")
    return telnet
Ejemplo n.º 5
0
 def genres(self):
     if not self.__genres:
         resp = self.__a_request("genres 0 255", raw=True)
         self.__genres = [v for k, v in self.__pairs_from(resp)
                          if k == 'genre']
         print_d(with_example("Loaded %d LMS genres", self.__genres))
     return self.__genres
Ejemplo n.º 6
0
    def __init__(self, transport, user=None, password=None,
                 cur_player_id=None, debug=False):

        self.transport = transport
        self._debug = debug
        self.user = user
        self.password = password
        if user and password:
            self.log_in()
            print_d("Authenticated with %s!" % self)
        self.players = {}
        self.refresh_status()
        players = list(self.players.values())
        if not players:
            raise SqueezeboxException("Uh-oh. No players found.")
        if not cur_player_id:
            self.cur_player_id = players[0].id
        elif cur_player_id not in self.players:
            print_w("Couldn't find player {id} (found: {all}). "
                    "Check your DEFAULT_PLAYER config.",
                    id=cur_player_id, all=", ".join(list(self.players.keys())))
            self.cur_player_id = players[0].id
        else:
            self.cur_player_id = cur_player_id
        print_d("Current player is now: {}", self.players[self.cur_player_id])
        self.__genres = []
        self.__playlists = []
        self.__favorites = []
Ejemplo n.º 7
0
    def __enter__(self):
        self.socket.settimeout(3)
        print_d("Creating test TCP server")
        self.thread = threading.Thread(target=self.serve_forever)

        print_d("Starting test server")
        self.thread.start()
        return self
Ejemplo n.º 8
0
 def stop(self):
     print_d("Killing {what}.", what=self)
     self.client.on_message = None
     self.client.on_subscribe = None
     # Don't unsubscribe-and-run (disconnect). Causes issues with brokers.
     # self.client.unsubscribe(self.resp_topic)
     self.client.disconnect()
     return super().stop()
Ejemplo n.º 9
0
 def handle(self):
     try:
         data = self.request.recv(1024).decode('utf-8')
     except UnicodeDecodeError:
         data = "(invalid)"
     response = response_for(data)
     print_d("> \"%s\"\n%s" % (data.strip(), response))
     self.request.sendall(response.encode('utf-8'))
Ejemplo n.º 10
0
 def favorites(self):
     if not self.__favorites:
         resp = self.__a_request("favorites items 0 255 want_url:1",
                                 raw=True)
         self.__favorites = {d['name']: d
                             for d in self._groups(resp, 'name')
                             if d['isaudio']}
         print_d(with_example("Loaded %d LMS faves", self.__favorites))
     return self.__favorites
Ejemplo n.º 11
0
 def __a_request(self, line, raw=False, wait=True):
     reply = self._request([line], raw=raw, wait=wait)
     if reply and len(reply):
         return reply[0]
     if self.user and self.password:
         print_d("Command failed. Trying to re-log in.")
         self.log_in()
         reply = self._request([line], raw=raw, wait=wait)
         if reply and len(reply):
             return reply[0]
     raise SqueezeboxException("Unprocessable command or login error")
Ejemplo n.º 12
0
 def test_all_handler(self):
     fake_output = FakeTransport()
     server = Server(transport=fake_output)
     alexa = SqueezeAlexa(server=server)
     for name, func in handler._handlers.items():
         print_d(">>> Testing %s() <<<" % func.__name__)
         session = {'sessionId': None}
         intent = {'requestId': 'abcd', 'slots': {}}
         raw = func(alexa, intent, session, None)
         response = raw['response']
         assert 'directives' in response or 'outputSpeech' in response
         assert 'shouldEndSession' in response
Ejemplo n.º 13
0
 def __new__(cls, *args, **kwargs):
     if not cls._INSTANCE:
         print_d("Creating new server instance")
         cls._INSTANCE = super().__new__(cls)
         cls._CREATION_TIME = time.time()
         return cls._INSTANCE
     if time.time() - cls._CREATION_TIME > cls._MAX_CACHE_SECS:
         print_d("Recreating stale server instance")
         del cls._INSTANCE
         cls._CREATION_TIME = time.time()
         cls._INSTANCE = super().__new__(cls)
     return cls._INSTANCE
Ejemplo n.º 14
0
    def connect(self, host=None, port=None, keepalive=30, bind_address=""):
        host = host or self.settings.hostname
        port = port or self.settings.port

        check_listening(host, port, msg="check your MQTT settings")
        ret = super().connect(host=host,
                              port=port,
                              keepalive=keepalive,
                              bind_address=bind_address)
        if MQTT_ERR_SUCCESS == ret:
            print_d("Connecting to {}", self.settings)
            return ret
        raise Error("Couldn't connect to {}".format(self.settings))
Ejemplo n.º 15
0
def speech_response(title=None,
                    speech=None,
                    reprompt_text=None,
                    end=True,
                    store=None,
                    text=None,
                    use_ssml=False):
    speechlet_response = speech_fragment(speech=speech or title,
                                         title=title,
                                         reprompt_text=reprompt_text,
                                         text=text,
                                         end=end,
                                         use_ssml=use_ssml)
    print_d("Returning {response}", response=speechlet_response)
    return _build_response(speechlet_response, store=store)
Ejemplo n.º 16
0
    def __init__(self, client: CustomClient, req_topic: str, resp_topic: str):
        def subscribed(client, userdata, mind, granted_qos):
            self.is_connected = True
            print_d("MQTT/TLS transport to {client} initialised. (@QoS {qos})",
                    client=client,
                    qos=granted_qos)

        super().__init__()
        self.client = client
        self.req_topic = req_topic
        self.resp_topic = resp_topic
        self.client.on_subscribe = subscribed
        self.client.on_message = self._on_message
        self.message = []
        print_d("Created transport: {self!r}", self=self)
Ejemplo n.º 17
0
    def create(self, mqtt_client=None):
        if self.mqtt_settings.configured:
            s = self.mqtt_settings
            print_d("Found MQTT config, so setting up MQTT transport.")
            client = mqtt_client or CustomClient(s)
            return MqttTransport(client,
                                 req_topic=s.topic_req,
                                 resp_topic=s.topic_resp)

        print_d("Defaulting to SSL transport")
        s = self.ssl_config
        return SslSocketTransport(hostname=s.server_hostname,
                                  port=s.port,
                                  ca_file=s.ca_file_path,
                                  cert_file=s.cert_file_path,
                                  verify_hostname=s.verify_server_hostname)
Ejemplo n.º 18
0
 def communicate(self, data, wait=True):
     stripped = data.rstrip('\n')
     if data.startswith('serverstatus'):
         print_d("Faking server status...")
         return (
             '{orig} player%20count:1 playerid:{pid} name:{name}\n'.format(
                 orig=stripped, name=self.player_name, pid=self.player_id))
     elif ' status ' in stripped:
         print_d("Faking player status...")
         return stripped + A_REAL_STATUS
     elif 'login ' in stripped:
         return 'login %s ******' % stripped.split()[1].replace(' ', '%20')
     elif ' time ' in data:
         return '%s %.3f' % (stripped.rstrip('?'), FAKE_LENGTH)
     elif 'favorites items ' in data:
         return stripped + REAL_FAVES
     return stripped + ' OK\n'
Ejemplo n.º 19
0
def create_transport(ssl_config=SSL_SETTINGS, mqtt_settings=MQTT_SETTINGS):
    if mqtt_settings.configured:
        s = mqtt_settings
        print_d("Found MQTT config, so setting up MQTT transport.")
        client = CustomClient(s)
        transport = MqttTransport(client,
                                  req_topic=s.TOPIC_REQ,
                                  resp_topic=s.TOPIC_RESP)
        transport.start()
        return transport

    print_d("Defaulting to SSL transport")
    s = ssl_config
    return SslSocketTransport(hostname=s.server_hostname,
                              port=s.port,
                              ca_file=s.ca_file_path,
                              cert_file=s.cert_file_path,
                              verify_hostname=s.verify_server_hostname)
Ejemplo n.º 20
0
    def _request(self, lines, raw=False, wait=True) -> List[str]:
        """
        Send multiple pipelined requests to the server, if connected,
        and return their responses,
        assuming order is maintained (which seems safe).
        """
        if not (lines and len(lines)):
            return []
        lines = [l.rstrip() for l in lines]

        first_word = lines[0].split()[0]
        if not (self.transport.is_connected or first_word == 'login'):
            raise SqueezeboxException(
                "Can't do '{cmd}', {transport} is not connected".format(
                    cmd=first_word, transport=self.transport))

        if self._debug:
            print_d("<<<< " + "\n..<< ".join(lines))
        request = "\n".join(lines)
        raw_response = self.transport.communicate(request, wait=wait)
        if not wait:
            return []
        if not raw_response:
            raise SqueezeboxException(
                "No further response from %s. Login problem?" % self)
        raw_response = raw_response.rstrip("\n")
        response = raw_response if raw else self._unquote(raw_response)
        if self._debug:
            print_d(">>>> " + "\n..>> ".join(response.splitlines()))

        def start_point(text):
            if first_word == 'login':
                return 6
            delta = -1 if text.endswith('?') else 1
            return len(self._unquote(text) if raw else text) + delta

        resp_lines = response.splitlines()
        if len(lines) != len(resp_lines):
            raise ValueError("Response problem: %s != %s" %
                             (lines, resp_lines))
        return [
            resp_line[start_point(line):]
            for line, resp_line in zip(lines, resp_lines)
        ]
Ejemplo n.º 21
0
 def communicate(self, data, wait=True):
     eof = False
     response = ''
     num_lines = data.count("\n")
     try:
         self._ssl_sock.sendall(data.encode('utf-8'))
         if not wait:
             return None
         while not eof:
             response += self._ssl_sock.recv().decode('utf-8')
             eof = response.count("\n") == num_lines or not response
         return response
     except socket.error as e:
         print_d("Couldn't communicate with Squeezebox ({!r})", e)
         self.failures += 1
         if self.failures >= self._MAX_FAILURES:
             print_w("Too many Squeezebox failures. Disconnecting")
             self.is_connected = False
         return None
Ejemplo n.º 22
0
    def communicate(self, raw: str, wait=True) -> str:
        data = raw.strip() + '\n'
        num_lines = data.count('\n')
        self._clear()
        ret = self.client.publish(self.req_topic,
                                  data.encode('utf-8'),
                                  qos=1 if wait else 0)
        if not wait:
            return None
        ret.wait_for_publish()
        if ret.rc != MQTT_ERR_SUCCESS:
            raise Error("Error publishing message: {}", error_string(ret.rc))
        print_d("Published to {topic} OK. Waiting for {num} line(s)...",
                topic=self.req_topic,
                num=num_lines)

        wait_for(lambda s: len(s.response_lines) >= num_lines,
                 context=self,
                 what="response from mqtt-squeeze",
                 timeout=5)
        return "\n".join(m.decode('utf-8') for m in self.response_lines)
Ejemplo n.º 23
0
    def __init__(self,
                 transport,
                 user=None,
                 password=None,
                 cur_player_id=None,
                 debug=False):

        self.transport = transport
        self._debug = debug
        self.user = user
        self.password = password
        if user and password:
            self.log_in()
            print_d("Authenticated with %s!" % self)
        self.players = {}
        self.refresh_status()
        self.cur_player_id = pid = cur_player_id or list(self.players)[0]
        print_d("Default player is now %s" % (self.players[pid], ))
        self.__genres = []
        self.__playlists = []
        self.__favorites = []
        self._created_time = time.time()
Ejemplo n.º 24
0
    def get_track_details(self, offset=0, player_id=None) -> Dict[str, List]:
        """Returns a dict of details,
        for current (offset=0) or future (offset>0) playlist tracks"""
        pid = player_id or self.cur_player_id
        index = offset + 1
        cmd = "status - %d tags:%s" % (index, DETAILS_TAGS)
        responses = self.player_request(cmd, pid, raw=True)
        items = next(self._groups(responses)).items()

        def values_for(tag: str, value: str) -> List[str]:
            return ([value] if tag in ('title', 'album') else
                    [v.strip() for v in value.split(',')])

        TAGS = {
            'title', 'genre', 'genres', 'album', 'trackartist', 'artist',
            'albumartist', 'composer'
        }
        details = {k: values_for(k, v) for k, v in items if v and k in TAGS}
        if 'genres' in details:
            details['genre'] = details['genres']
            del details['genres']
        print_d("Processed details: {d}", d=details)
        return details
Ejemplo n.º 25
0
    def start(self):
        def connected(client, userdata, flags, rc):
            print_d("Connected to {client}. Subscribing to {topic}",
                    client=self.client,
                    topic=self.resp_topic)
            result, mid = self.client.subscribe(self.resp_topic, qos=1)
            if result != MQTT_ERR_SUCCESS:
                raise Error("Couldn't subscribe to '{topic}'", self.resp_topic)

        def disconnected(client, userdata, rc):
            print_d("Disconnected from {client}", client=self.client)
            self.is_connected = False

        self.is_connected = self.client.connected
        if self.is_connected:
            print_d("Already connected, great!")
            return
        self.client.on_connect = connected
        self.client.on_disconnect = disconnected
        assert self.client.loop_start() != MQTT_ERR_INVAL
        self.client.connect()
        wait_for(lambda s: s.is_connected, what="connection", context=self)
        return self
Ejemplo n.º 26
0
    def _request(self, lines, raw=False, wait=True) -> List[str]:
        """
        Send multiple pipelined requests to the server, if connected,
        and return their responses,
        assuming order is maintained (which seems safe).
        """
        if not (lines and len(lines)):
            return []
        lines = [l.rstrip() for l in lines]

        match = RESPONSE_CMD_REGEX.match(lines[0])
        # If we can't match, then take the first two words (for debugging)
        first_word = (match.group(2)
                      if match else ' '.join(lines[0].split()[:2]))
        if not (self.transport.is_connected or first_word == 'login'):
            try:
                print_w("Transport wasn't connected - trying to restart")
                self.transport.start()
            except Exception:
                raise SqueezeboxException(
                    "Can't do '{cmd}', {transport} is not connected".format(
                        cmd=first_word, transport=self.transport))

        if self._debug:
            print_d("<<<< " + "\n..<< ".join(lines))
        request = "\n".join(lines)
        raw_response = self.transport.communicate(request, wait=wait)
        if not wait:
            return []
        if not raw_response:
            raise SqueezeboxException(
                "No further response from %s. Login problem?" % self)
        raw_response = raw_response.rstrip("\n")
        response = raw_response if raw else self._unquote(raw_response)
        if self._debug:
            print_d(">>>> " + "\n..>> ".join(response.splitlines()))

        def start_point(text):
            if first_word == 'login':
                return 6
            delta = -1 if text.endswith('?') else 1
            return len(self._unquote(text) if raw else text) + delta

        resp_lines = response.splitlines()
        if len(lines) != len(resp_lines):
            print_d("Got mismatched response: {lines} vs {resp_lines}",
                    lines=lines,
                    resp_lines=resp_lines)
            raise Error("Transport response problem: got %d lines, not %d" %
                        (len(resp_lines), len(lines)))
        return [
            resp_line[start_point(line):]
            for line, resp_line in zip(lines, resp_lines)
        ]
Ejemplo n.º 27
0
 def refresh_status(self):
     """ Updates the list of the Squeezebox players available and other
     server metadata."""
     print_d("Refreshing server and player statuses...")
     response = self.__a_request("serverstatus 0 99", raw=True)
     self.players = {}
     for data in self._groups(response, 'playerid',
                              extra_bools=['power', 'connected']):
         self.players[data['playerid']] = SqueezeboxPlayerSettings(data)
     print_d("Found %d player(s): %s"
             % (len(self.players),
                [p['name'] for p in self.players.values()]))
     if self._debug:
         print_d("Player(s): %s", self.players.values())
Ejemplo n.º 28
0
 def refresh_status(self):
     """ Updates the list of the Squeezebox players available and other
     server metadata."""
     print_d("Refreshing server and player statuses.")
     response = self.__a_request("serverstatus 0 99", raw=True)
     self.players = {}
     for data in self._groups(response, 'playerid',
                              extra_bools=['power', 'connected']):
         if data.get('connected', False):
             self.players[data['playerid']] = SqueezeboxPlayerSettings(data)
     print_d("Found {total} connected player(s): {players}",
             total=len(self.players),
             players=[p.get('name', _("Unknown player"))
                      for p in self.players.values()])
     if self._debug:
         print_d("Player(s): {players}", players=self.players.values())
Ejemplo n.º 29
0
def on_message(client, userdata, message):
    num_lines = message.payload.count(b'\n')
    msg = message.payload.decode('utf-8')
    if DEBUG:
        print_d(">>> {msg} (@QoS {qos})", msg=msg.strip(), qos=message.qos)
    telnet.write(message.payload.strip() + b'\n')
    resp_lines = []
    while len(resp_lines) < num_lines:
        resp_lines.append(telnet.read_until(b'\n').strip())

    rsp = b'\n'.join(resp_lines)
    if rsp:
        if DEBUG:
            print_d("<<< {}", rsp.decode('utf-8'))
        client.publish(MQTT_SETTINGS.topic_resp, rsp, qos=1)
    else:
        print_d("No reply")
Ejemplo n.º 30
0
    def create(self, *args, **kwargs):
        instance = type(self)._INSTANCE
        if instance and self._too_old():
            print_d("Killing stale server instance.")
            instance.disconnect()
            instance = self._INSTANCE = None
            # Fall through

        if not instance or not instance.connected:
            print_d("Creating new server instance")
            transport = self.transport_factory.create()
            transport.start()
            inst = type(self)._INSTANCE = Server(transport, *args, **kwargs)
            type(self)._CREATION_TIME = time.time()
            return inst
        print_d("Reusing cached instance {object}", object=instance)
        return instance