Announcement

Collapse
No announcement yet.

Help: Implementing websockets in a plugin

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Help: Implementing websockets in a plugin

    > It would make sense to add it in 8.1 as I think it is right to have the
    > module kept up to date as AnyEvent provides flexibility and I think
    > there are a few other uses for WebSockets.


    It would be crucial to be able to use the same code base for old and
    new. Or we'd have to drop support for most platforms, as we wouldn't be
    able to compile the required binaries for many of them. In particular
    Windows could be the end.

    --

    Michael
    sigpic

    ALEXA LMS SKILL: http://www.hab-tunes.com | Twitter: #habtunes
    Personal HA BLOG: http://mediaserver8.blogspot.com

    Squeezebox x2 | Squeezebox Radio x 2 | Squeezebox Duet x2

    #2
    Help: Implementing websockets in a plugin

    Still working on the Alexa for Squeezebox plugin and, while it's working well with a local proxy built in to the plugin, this still requires an open port and we're all aware of the risk there (even though this does not expose any LMS ports).

    Anyway, I'd hoped to implement an alternative that would not require an open port. The options are MQTT or WebSockets. I went for the later as the server side implementation in Node-Red was trivial. However, I'm running into problems building a websocket client inside my LMS plugin and wondered if anyone had encountered/solved similar or had a suggestion for an alternative ws implementation approach.

    (I have posted the remaining section to PerlMonks but thought I'd cross-post here as I might get more LMS-focused responses.)



    I have a WebSocket server application built in Node-Red sitting behind an Apache reverse-proxy configuration. (The server is very simple, responding only to 'Ping' commands with an 'OK' response.)

    Using the 'Simple Websocket Client' extension for Google Chrome, I can successfully open the server URL, send the 'Ping' request and receive the 'OK' response, so I know that the server setup works.

    However, attempting to implement the same in PERL via AnyEvent:Socket, I consistently receive a 'No such device or address' error.(AnyEvent is correctly installed via the lib folder in my plugin).

    The relevant section of my PERL code looks like this;

    Code:
    sub sockInit{
    
    use AnyEvent::WebSocket::Client 0.12;
    use URI
    
    my $client = AnyEvent::WebSocket::Client->new( );
    
        my $uri = URI->new('ws://mydomain.com/ws/test');
    
        $client->connect($uri)->cb(sub {
     
          our $connection = eval { shift->recv };
             if([email protected]) {
              # handle error...
              myDebug([email protected]);
              return;
         }
       
        # send a message through the websocket...
        # $connection->send('ping');
       
       });
    
      }
    Ultimately, this fails in this section of AnyEvent/WebSocket/Client.pm (around line 90);

    Code:
    AnyEvent::Socket::tcp_connect $uri->host, $uri->port, sub {
        AnyEvent::Socket::tcp_connect $uri->as_string, 80, sub {
        my $fh = shift;
        unless($fh)
        {
          $done->croak("unable to connect to ".$uri->as_string." $!");
          return;
        }
    I've made some small edits there to get around issues with $uri->host and $uri->port not existing for URI::_generic which is what ws protocol comes through as. I've also extended the croak message to include the uri)


    Here's what it typically generates;

    unable to connect to ws://mydomain.com/ws/test No such device or address at /usr/share/squeezeboxserver/Plugins/Alexa/HabProxy.pm line 201

    This code runs inside a plugin I'm writing for the Logitech SqueezeServer platform)


    The relevant part of my Apache hosts config includes these lines in <VirtualHost *.80>

    Code:
    ProxyPass /ws ws://127.0.0.1:1880/ws
    ProxyPassReverse  /ws ws://127.0.0.1:1880/ws
    As noted, I know this to be working. Ultimately, I need this to work with user authentication in Apache against a DB and with WSS: but I've simplified extensively here to see if I can track down why my Perl implementation might be failing but it works fine in other clients.
    Last edited by meep; 2017-03-27, 16:17. Reason: code tags / cleanup
    sigpic

    ALEXA LMS SKILL: http://www.hab-tunes.com | Twitter: #habtunes
    Personal HA BLOG: http://mediaserver8.blogspot.com

    Squeezebox x2 | Squeezebox Radio x 2 | Squeezebox Duet x2

    Comment


      #3
      Help: Implementing websockets in a plugin

      > However, attempting to implement the same in PERL via 'AnyEvent:Socket'
      > (http://search.cpan.org/~plicease/Any...cket/Client.pm),
      > I consistently receive a 'No such device or address' error.(AnyEvent is
      > correctly installed via the lib folder in my plugin).


      You shouldn't need to install AnyEvent in your plugin, as LMS is based
      on it already. Alas I wouldn't be surprised if the version we're using
      was outdated. And if that was actually what your code was using, rather
      than yours.

      > Ultimately, this fails in this section of AnyEvent/WebSocket/Client.pm
      > (around line 90);
      > #AnyEvent::Socket::tcp_connect $uri->host, $uri->port, sub {
      > AnyEvent::Socket::tcp_connect $uri->as_string, 80, sub {
      > my $fh = shift;
      > unless($fh)
      > {
      > $done->croak("unable to connect to ".$uri->as_string." $!");
      > return;
      > }


      Aren't there dependencies for modules to support the ws protocol? Did
      you add them as well?

      --

      Michael
      Michael

      "It doesn't work - what shall I do?" - "Please check your server.log and/or scanner.log file!"
      (LMS: Settings/Information)

      Comment


        #4
        Many thanks for that Michael

        I'm definitely using the current version of AnyEvent in my plugin lib folder as I can make code changes to those files and see it have an effect.

        However, it's still possible there's a conflict there somewhere. I also believe I have all the dependencies in place as initially, there were a good number of errors relating to missing files etc. but all now resolved. I will check this further though.

        Right now, my plan is to build the simple websocket client in PERL outside the LMS plugin context to see if it's an issue with the libs or the libs inside LMS. Then, I'll try removing the AnyEvent packages from my plugin folder to see if I can call back on those used in LMS.

        I'll report back on what I find.

        Peter
        sigpic

        ALEXA LMS SKILL: http://www.hab-tunes.com | Twitter: #habtunes
        Personal HA BLOG: http://mediaserver8.blogspot.com

        Squeezebox x2 | Squeezebox Radio x 2 | Squeezebox Duet x2

        Comment


          #5
          So I gave up implementing a standalone test as it's beyond my PERL abilities. However, I see that the LMS AnyEvent is at version 5.x and doesn't include the WebSockets packages. The plugin does indeed run this older version, with only the additional, newer WebSockets code running from the plugin lib folder. I don't see any way to override the older AnyEvent core packages in the LMS with the newer versions and I'd guess this would be a very bad thing in any case.

          I'll try to implement an alternative WebSockets solution with Mojolicious or some other solution that won't conflict with LMS.

          Peter
          sigpic

          ALEXA LMS SKILL: http://www.hab-tunes.com | Twitter: #habtunes
          Personal HA BLOG: http://mediaserver8.blogspot.com

          Squeezebox x2 | Squeezebox Radio x 2 | Squeezebox Duet x2

          Comment


            #6
            cc

            Looks like Websocket clients just won't work in the context of LMS.

            The obvious way would be AnyEvent::Websocket::Client but this is at version 7.x and the AnyEvent libs in LMS are at 5.x and don't include WebSocket support. Adding the WebSocket package and dependencies to my plugin lib folder works OK but attempting to construct sockets fails as described above, likely due to version conflicts.

            I also tried Mojo::Websocket and Net:;Async::Websocket but both of these seem to require their own loops and implementing causes LMS to quit :-( Couldn't get them to function at all with Slim::Networking.

            I guess I could try to build my own Websocket implementation using lower-level libs but my Perl is just not up to that.

            I'm moving on to Anyevent::MQTT which is showing a little more promise but will be more involved on the server side, unfortunately.
            sigpic

            ALEXA LMS SKILL: http://www.hab-tunes.com | Twitter: #habtunes
            Personal HA BLOG: http://mediaserver8.blogspot.com

            Squeezebox x2 | Squeezebox Radio x 2 | Squeezebox Duet x2

            Comment


              #7
              For anyone who trips across this in the future, I got MQTT working through Net::MQTT::Message and IO::Socket::INET which will co-operate with Slim::Utils::Network::blocking and Slim::Networking::Select
              sigpic

              ALEXA LMS SKILL: http://www.hab-tunes.com | Twitter: #habtunes
              Personal HA BLOG: http://mediaserver8.blogspot.com

              Squeezebox x2 | Squeezebox Radio x 2 | Squeezebox Duet x2

              Comment


                #8
                Bringing this up again as I have come across another place that could use WebSockets as a solution.
                I was looking at a radio station to grab Now Playing info and they use WebSockets for it - and they do not seem to have a simple HTTP alternative.

                Perhaps updating AnyEvent from 5.26 (2010) to 7.x could be part of LMS 8.1
                I recognise that it is a big jump though and backwards compatibility may well make it impossible to do without another good reason.

                Paul Webster
                Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
                and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

                Comment


                  #9
                  Originally posted by Paul Webster View Post
                  Bringing this up again as I have come across another place that could use WebSockets as a solution.
                  I was looking at a radio station to grab Now Playing info and they use WebSockets for it - and they do not seem to have a simple HTTP alternative.
                  ClassicFM ?

                  Perhaps updating AnyEvent from 5.26 (2010) to 7.x could be part of LMS 8.1
                  I recognise that it is a big jump though and backwards compatibility may well make it impossible to do without another good reason.
                  A few years ago, when HLS & DASH needed HTTP 1.1 (before philippe added it to LMS) and so I used the AnyEvent module. I thought about the need to update the LMS AnyEvent and did some investigation but shied away.

                  It would make sense to add it in 8.1 as I think it is right to have the module kept up to date as AnyEvent provides flexibility and I think there are a few other uses for WebSockets.

                  I might look into it to assess feasibility and trouble spots so that we can understand possible implications.

                  Comment


                    #10
                    Originally posted by bpa View Post
                    ClassicFM ?
                    Yes - because of the forum post earlier today.
                    It looks straight-forward to handle the data.

                    sending
                    Code:
                    {"actions":[{"type":"subscribe","service":"121"}]}
                    returns useful json - plus it is GlobalRadio group so I presume that lots (all?) of the stations in the group are using it (each with a different service id).

                    They do not appear to be sending Now Playing track info to TuneIn (so not in LMS), RadioPlayer or last.fm

                    However, I just noticed that they are (or someone is) sending it to radio.net ... so maybe you could grab it from there (although no artwork).
                    radio.net have an API that is very similar to TuneIn that broadcasters can use to send Now Playing info to (I use both from the Internet radio station that I am involved with).
                    Probably discussion of that sort of thing is best on your radio.net thread.
                    Paul Webster
                    Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
                    and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

                    Comment


                      #11
                      Originally posted by Paul Webster View Post
                      Yes - because of the forum post earlier today.
                      I checked ClassicFM a while ago and noticed "wss://" - again check again today as a result of the post.

                      A few years ago, I had a Classicfm plugin for their live & "listen Again" but they changed and haven't maintained it.



                      it is GlobalRadio group so I presume that lots (all?) of the stations in the group are using it (each with a different service id).
                      Always a big plus when station is part of a group.

                      However, I just noticed that they are (or someone is) sending it to radio.net ... so maybe you could grab it from there (although no artwork).
                      radio.net have an API that is very similar to TuneIn that broadcasters can use to send Now Playing info to (I use both from the Internet radio station that I am involved with).
                      Probably discussion of that sort of thing is best on your radio.net thread.
                      I have avoided doing "live" info for radio.net - as they changed their layout a few times, removed some info from station list and seemed to be in flux and . So wasn't sure whether access would continue.
                      IIRC Their "Live info" support was patchy which if it is still so, I rather not offer as it is confusing for users who may think plugin is broken.

                      Comment


                        #12
                        Originally posted by Paul Webster View Post
                        Bringing this up again as I have come across another place that could use WebSockets as a solution.
                        I was looking at a radio station to grab Now Playing info and they use WebSockets for it - and they do not seem to have a simple HTTP alternative.

                        Perhaps updating AnyEvent from 5.26 (2010) to 7.x could be part of LMS 8.1
                        I recognise that it is a big jump though and backwards compatibility may well make it impossible to do without another good reason.

                        https://metacpan.org/changes/distribution/AnyEvent
                        Bringing up this WebSockets discussion again. I've needed to look at this as I'm taking the GlobalPlayer plugin to the next stage, implementing using hls streams for live radio which should improve the bitrates and enable introducing similar pause/rewind/restart functionality similar to the BBC Sounds functionality. Unfortunately most of the information needed comes down websockets so I had no choice but to look at it.
                        I've managed to create a non-blocking websockets class that works fine for my needs, it uses
                        Code:
                        use IO::Socket::SSL;
                        use IO::Select;
                        use Protocol::WebSocket::Client;
                        So I only needed to include the low level Protocol::WebSocket::Client lib. No need for AnyEvent

                        It works for my use case when I only want to wait for a message after sending, then close the connection. It will need another method to continually listen on a socket, if you need that, but that should not be too tricky. I thought I would share with you @Paul Webster, as I know that's something you have expressed would be useful for your plugins. The branch where I am working on it is here : https://github.com/expectingtofly/LM...cketHandler.pm


                        I use it by doing something like:
                        Code:
                        	my $ws = Plugins::GlobalPlayerUK::WebSocketHandler->new();
                        	$ws->wsconnect(
                        		'wss://some.url.com/v2/now-playing',
                        		sub {#success
                        			$ws->wssend('{"actions":[{"type":"subscribe","service":"some service"}]}');
                        			$ws->wsreceive(
                        				0.1,
                        				sub {
                        					$log->info("Read succeeded");
                        				},
                        				sub {
                        					$log->error("Read failed");
                        				}
                        			);
                        		},
                        		sub {#fail
                        			$log->error("Failed to connect to web socket");
                        		},
                        		sub {#Read
                        			my $readin = shift;
                        			$log->info("We have read ". $readin);  #Do something here with what came in
                        			$ws->wssend('{"actions":[{"type":"unsubscribe","stream_id":"Some service"}]}');
                        			$ws->wsclose();
                        		}
                        	);


                        credit to this guy, for clues and code snippets
                        Last edited by expectingtofly; 2022-12-15, 16:36.
                        Stuart McLean

                        ExpectingToFly Plugins :
                        BBC Sounds, Global Player (UK), Times Radio, UK Radio Player, Virgin Radio (UK) and the Radio Favourites Plugin

                        For BBC Sounds help see the BBC Sounds Wiki.

                        Comment

                        Working...
                        X