Skip to content

Instantly share code, notes, and snippets.

@KurtJacobson
Created January 22, 2018 20:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KurtJacobson/6900038875cd4d073e1f760c615cb773 to your computer and use it in GitHub Desktop.
Save KurtJacobson/6900038875cd4d073e1f760c615cb773 to your computer and use it in GitHub Desktop.
WebSocket server extension for WeeWx for real time weather data without having to refresh web page.
<!DOCTYPE html>
<html>
<head>
<title>WebSocket</title>
</head>
<body>
<h2>WeeWx WebSocket Example</h2>
<p>Example of real time weather data display using WebSockets. The data is
updated on each LOOP packet.
</p>
<p>
<b>WS status: </b><span id="status">Connecting...</span>
</p>
<p><b>Real time data</b></p>
<p>
WindDir: <span id="data">----</span>
</p>
<p><b>Event Log</b></p>
<div id="log"></div>
<!-- Import jquery -->
<script src="http://code.jquery.com/jquery.min.js"></script>
<script>
// log function
log = function(data){
$("div#log").prepend(data + '</br>');
console.log(data);
document.getElementById("status").innerHTML = data;
};
$(document).ready(function () {
var isConnected = false;
var ws;
// create websocket instance
ws = new WebSocket("ws://localhost/ws");
// Handle incoming websocket message callback
ws.onmessage = function(evt) {
var dataIn = JSON.parse(evt.data);
document.getElementById("data").innerHTML = dataIn.windDir;
};
// Close Websocket callback
ws.onclose = function(evt) {
//alert("Connection close");
log('Conection closed');
};
// Open Websocket callback
ws.onopen = function(evt) {
//alert("Connection opened");
log('Connection opened');
};
// Error Websocket callback
ws.error = function(err){
log('Connection error!!!');
}
});
</script>
</body>
</html>
# Copyright (c) 2017 Kurt Jacobson <kurtcjacobson@gmail.com>
# See the file LICENSE.txt for your rights.
"""WebSocket server extension for WeeWx.
Used for updating a web-page on each LOOP packet for real-time data without
having to reload the entire page.
*******************************************************************************
To use this extension, add the following somewhere in your weewx.conf file:
[socketServer]
port = 8888 # The port to listen on
uri = /ws # The uri were the WS is located
*******************************************************************************
To enable this service:
1) place this file to the user directory
2) modify the weewx configuration file by adding this service to the option
"report_services", located in section [Engine][[Services]].
[Engine]
[[Services]]
...
report_services = weewx.engine.StdPrint, weewx.engine.StdReport, user.socketServer.socketServer
3) configure your web server for WebSockets. This example is for nginx.
# Add for websockets
# https://stackoverflow.com/questions/42491668/nginx-forward-websocket-from-80-to-websocket-port
location /ws {
proxy_pass http://localhost:8888;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
*******************************************************************************
"""
# General imports
import time
import syslog
import threading
# SocketServer imports
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
# WeeWX imports
import weewx
from weewx.engine import StdService
from weeutil.weeutil import timestamp_to_string, option_as_list
# Globals
data = {}
clients = []
# Inherit from the base class StdService:
class socketServer(StdService):
"""Websocket server to send LOOP packet for real-time update of web pages."""
def __init__(self, engine, config_dict):
# Pass the initialization information on to the superclass:
super(socketServer, self).__init__(engine, config_dict)
try:
# Dig the needed options out of the configuration dictionary.
# If a critical option is missing, an exception will be thrown.
self.port = int(config_dict['socketServer'].get('port', '8888'))
self.uri = config_dict['socketServer'].get('uri', '/ws')
syslog.syslog(syslog.LOG_INFO, "socketServer: socketServer enabled. Listening at port %d" % self.port)
self.startServer()
# If we got this far, it's OK to start intercepting events:
self.bind(weewx.NEW_LOOP_PACKET, self.newLoopPacket)
self.bind(weewx.NEW_ARCHIVE_RECORD, self.newArchiveRecord)
except KeyError, e:
syslog.syslog(syslog.LOG_INFO, "socketServer: ERROR: Missing required config parameter: %s" % e)
def startServer(self):
application = tornado.web.Application([(self.uri, WSHandler),])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(self.port)
myIP = socket.gethostbyname(socket.gethostname())
print 'Websocket Server Started at http:\\\%s:%i' % (myIP, self.port)
i = tornado.ioloop.IOLoop.instance()
t = threading.Thread(target = i.start)
t.start()
def newLoopPacket(self, event):
"""This function is called on each new LOOP packet."""
data = event.packet
data['connectedClients'] = len(clients)
for client in clients:
client.write_message(data)
def newArchiveRecord(self, event):
"""This function is called on each new archive record."""
pass
def shutDown(self):
"""This function is called on shutdown to cleanly stop the tornado loop."""
print 'Shutting down socketServer...'
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_callback(ioloop.stop)
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('new connection')
if self not in clients:
clients.append(self)
return True
def on_message(self, message):
print('message received %s' % message)
return True
def on_close(self):
print('connection closed')
if self in clients:
clients.remove(self)
return True
def check_origin(self, origin):
print 'Check origin: ', origin
return True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment