Phoenix Channel sending messages from a client outside the project

I wanted to send a message to my user channel of my Phoenix Application. I have joined a user_token with the channel as users:user_token in the user_channel.ex . I was successful doing it from another controller called the toy_controller by calling a broadcast method. The broadcast method is in the user channel. And I have written a jQuery file to handle the events. I was looking for something which can send messages to the same channel from outside of the project, because I wanted to do some IoT stuff. I have tried a python module called occamy.socket and the JS client of Phoenix that it uses internally. Then, I found a disconnection always. I can’t figure out the exact address of the websocket connection from Phoenix. If I am trying it with that Phoenix npm library in that project folder itself, it says ReferenceError: window is not defined always. And, I think it is because of the initialization part of the socket in the web/static/js/socket.js file where it’s written as

let socket = new Socket("/socket", {params: {token: window.userToken}})                              

, but I am not sure. The thing that I have tried is below

var Socket = require("phoenix-socket").Socket;
var socket = new Socket("ws://localhost:4000/socket");

In the python client, I was also trying to connect to this address and got a disconnection error. I want to do it for IoT purposes, where I want to monitor sensor data of a user. Each user will be having their own sensors to be monitored. So, I have configured the channel topic:subtopic channel as users:user_token . I need to send messages from my raspberry pi to this channel using those unique tokens of the users. My user_channel, user.js, app.js and socket.js are given below.

//web/static/js/socket.js
import {Socket} from "phoenix"

let socket = new Socket("/socket", {params: {token: window.userToken}})


socket.connect()


export default socket
//web/static/app.js


import "phoenix_html"
import user from "./user"
#web/channels/user_channel.ex
defmodule Tworit.UserChannel do
    use Tworit.Web, :channel

    def join("users:" <> user_token, payload, socket) do
        if authorized?(payload) do
          {:ok, "Joined To User:#{user_token}", socket}
      else
          {:error, %{reason: "unauthorized"}}
      end
    end


    def handle_in("ping", payload, socket) do
        {:reply, {:ok, payload}, socket}
    end


    def handle_in("shout", payload, socket) do
       broadcast socket, "shout", payload
       {:noreply, socket}
    end


    def handle_out(event, payload, socket) do
        push socket, event, payload
        {:noreply, socket}
    end


     defp authorized?(_payload) do
       true
     end

    def broadcast_change(toy, current_user) do
       payload = %{
        "name" => toy.name,
        "body" => toy.body
       }
      Tworit.Endpoint.broadcast("users:#{current_user.token}", "change", payload)
    end

end
//web/static/js/user.js
import socket from "./socket"

$(function() {
  let ul = $("ul#em")

  if (ul.length) {
    var token = ul.data("id")
    var topic = "users:" + token
		
    // Join the topic
    let channel = socket.channel(topic, {})
    channel.join()
      .receive("ok", data => {
        console.log("Joined topic", topic)
      })
      .receive("error", resp => {
        console.log("Unable to join topic", topic)
      })
    channel.on("change", toy => {
	    console.log("Change:", toy);
	    $("#message").append(toy["name"])
    })
  }
});

window is not defined if it is not running it within a browser context. The Phoenix Javascript library only runs in a browser session, so you could emulate it enough perhaps? Honestly though, if you are using Python then just get a websocket library for Python and connect straight (and if any issues with it then give us the code too). The Phoenix websocket API is dead-simple if you look at its javascript. :slight_smile:

Yeah I got you as far as the JS client is concerned. Thanks ! I will try to connect it from a simple python web-socket client. :smiley:

I was successful in sending a message repeatedly from a python client to the phoenix channel. :smiley:

#!/usr/bin/env python
import asyncio
import websockets
import json





async def hello():
	async with websockets.connect('ws://127.0.0.1:4000/socket/websocket') as websocket:
		data = dict(topic="users:my_token", event="phx_join", payload={}, ref=None)
		await websocket.send(json.dumps(data))
		# print("joined")
		#greeting = await websocket.recv()
		print("Joined")
		while True:
			msg = dict(topic="users:my_token", event="shout", payload={"body":"tworitdash"}, ref=None)
			await websocket.send(json.dumps(msg))
			call = await websocket.recv()
			print("< {}".format(call))




asyncio.get_event_loop().run_until_complete(hello())
asyncio.get_event_loop().run_forever()

But I don’t understand the argument called ref here. What does that mean and what it should be? I am sending a None all the time. Thank you in advance ! And I will try to make the communication bidirectional for the python client.

It is used as a ‘ref’ like in Erlang/Elixir, a unique ID (just an integer in phoenix’s case I think) so that the server can respond back to a message. When you do a message and listen for a response from that call (instead of just waiting on a new message) it uses the ref to reference that same message. If you use unique calls for every function and never use a response call then I do not ‘think’ it would be necessary to implement.

EDIT: Ah, yes indeed, here is where ref is mutated for a call, search for makeRef’s elsewhere in the file to see its usage: https://github.com/phoenixframework/phoenix/blob/master/priv/static/phoenix.js#L852

1 Like

Thank you so much again. :slight_smile:

Hey @tworitdash do you have a github repo somewhere with both sides of this working (elixir and python)?