Phoenix Channel sending messages from a client outside the project
Question: 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"])
        })
      }
    });


Answer: Finally, I am able to send and receive messages asynchronously from a python
program. It uses websockets asyncio module from python. I figured out the
various events required for phoenix channels like 'phx_join' for joining a
topic and all. So, the following program worked.

    
    
    import asyncio
    import websockets
    import json
    import time
    from random import randint
    import serial
    
    from pyfirmata import Arduino, util
    
    board = Arduino('/dev/ttyACM1')
    
    it = util.Iterator(board)
    it.start()
    board.analog[0].enable_reporting()
    board.analog[1].enable_reporting()
    board.analog[2].enable_reporting()
    board.analog[3].enable_reporting()
    
    import RPi.GPIO as gpio
    
    gpio.setmode(gpio.BCM)
    gpio.setup(14, gpio.OUT)
    
    
    async def main():
        async with websockets.connect('ws://IP_addr:4000/socket/websocket') as websocket:
            data = dict(topic="users:user_token", event="phx_join", payload={}, ref=None)
            #this method joins the phoenix channel
            await websocket.send(json.dumps(data))
    
            print("Joined")
    
            while True:
                msg = await retrieve() # waits for data from arduino analog pins
                await websocket.send(json.dumps(msg)) # sends the sensor output to phoenix channel
    
                print("sent")
                call = await websocket.recv() # waits for anything from the phoenix server
                control = json.loads(call)
    
                # I have sent values from 2 buttons for swicthing a led with event 'control'
    
                if(control['event'] == "control"):
                    event(control['payload']['val']) #swiches the led as per the input from event 'control'
    
    
                print("< {}".format(call))
    
    def event(val):
        if(val == "on"):
            gpio.output(14, True)
        if(val == "off"):
            gpio.output(14, False)
    
    async def retrieve():
        #analog read
        load = board.analog[0].read()
        pf = board.analog[1].read()
        reading = board.analog[2].read()
        thd = board.analog[3].read()
        output = {"load": load, "pf": pf, "reading": reading,"thd": thd}
    
        msg = dict(topic="users:user_token", event="sensor_output", payload=output, ref=None) # with 
        #event "sensor_outputs" 
        #the phoenix server displays the data on to a page.
    
        print(msg)
        return(msg)
    
    asyncio.get_event_loop().run_until_complete(main())
    asyncio.get_event_loop().run_forever()
    

