Created
January 22, 2018 20:36
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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