FEATURE: Add unregister action
This commit is contained in:
parent
fbe9560458
commit
5858745c1d
|
@ -7,7 +7,8 @@ import os
|
|||
import mopidy
|
||||
|
||||
from .frontend import PummeluffFrontend
|
||||
from .web import LatestHandler, RegistryHandler, RegisterHandler, ActionClassesHandler
|
||||
from .web import LatestHandler, RegistryHandler, RegisterHandler, UnregisterHandler, \
|
||||
ActionClassesHandler
|
||||
|
||||
|
||||
def app_factory(config, core): # pylint: disable=unused-argument
|
||||
|
@ -21,10 +22,11 @@ def app_factory(config, core): # pylint: disable=unused-argument
|
|||
:rtype: list
|
||||
'''
|
||||
return [
|
||||
('/latest/', LatestHandler, {'core': core}),
|
||||
('/registry/', RegistryHandler, {'core': core}),
|
||||
('/register/', RegisterHandler, {'core': core}),
|
||||
('/action-classes/', ActionClassesHandler, {'core': core}),
|
||||
('/latest/', LatestHandler),
|
||||
('/registry/', RegistryHandler),
|
||||
('/register/', RegisterHandler),
|
||||
('/unregister/', UnregisterHandler),
|
||||
('/action-classes/', ActionClassesHandler),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -127,5 +127,16 @@ class RegistryDict(dict):
|
|||
|
||||
return action
|
||||
|
||||
def unregister(self, uid):
|
||||
'''
|
||||
Unregister a tag from the registry.
|
||||
|
||||
:param str uid: The UID
|
||||
'''
|
||||
LOGGER.info('Unregistering tag %s', uid)
|
||||
|
||||
del self[uid]
|
||||
self.write()
|
||||
|
||||
|
||||
REGISTRY = RegistryDict()
|
||||
|
|
|
@ -6,6 +6,7 @@ __all__ = (
|
|||
'LatestHandler',
|
||||
'RegistryHandler',
|
||||
'RegisterHandler',
|
||||
'UnregisterHandler',
|
||||
'ActionClassesHandler',
|
||||
)
|
||||
|
||||
|
@ -26,14 +27,6 @@ class LatestHandler(RequestHandler): # pylint: disable=abstract-method
|
|||
Request handler which returns the latest scanned tag.
|
||||
'''
|
||||
|
||||
def initialize(self, core): # pylint: disable=arguments-differ
|
||||
'''
|
||||
Initialize request handler with Mopidy core.
|
||||
|
||||
:param mopidy.core.Core mopidy_core: The mopidy core instance
|
||||
'''
|
||||
self.core = core # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def get(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Handle GET request.
|
||||
|
@ -65,14 +58,6 @@ class RegistryHandler(RequestHandler): # pylint: disable=abstract-method
|
|||
Request handler which returns all registered tags.
|
||||
'''
|
||||
|
||||
def initialize(self, core): # pylint: disable=arguments-differ
|
||||
'''
|
||||
Initialize request handler with Mopidy core.
|
||||
|
||||
:param mopidy.core.Core mopidy_core: The mopidy core instance
|
||||
'''
|
||||
self.core = core # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def get(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Handle GET request.
|
||||
|
@ -97,14 +82,6 @@ class RegisterHandler(RequestHandler): # pylint: disable=abstract-method
|
|||
Request handler which registers an RFID tag in the registry.
|
||||
'''
|
||||
|
||||
def initialize(self, core): # pylint: disable=arguments-differ
|
||||
'''
|
||||
Initialize request handler with Mopidy core.
|
||||
|
||||
:param mopidy.core.Core mopidy_core: The mopidy core instance
|
||||
'''
|
||||
self.core = core # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def post(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Handle POST request.
|
||||
|
@ -141,19 +118,45 @@ class RegisterHandler(RequestHandler): # pylint: disable=abstract-method
|
|||
self.post()
|
||||
|
||||
|
||||
class UnregisterHandler(RequestHandler): # pylint: disable=abstract-method
|
||||
'''
|
||||
Request handler which unregisters an RFID tag from the registry.
|
||||
'''
|
||||
|
||||
def post(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Handle POST request.
|
||||
'''
|
||||
try:
|
||||
REGISTRY.unregister(uid=self.get_argument('uid'))
|
||||
|
||||
data = {
|
||||
'success': True,
|
||||
'message': 'Tag successfully unregistered',
|
||||
}
|
||||
|
||||
except ValueError as ex:
|
||||
self.set_status(400)
|
||||
data = {
|
||||
'success': False,
|
||||
'message': str(ex)
|
||||
}
|
||||
|
||||
self.set_header('Content-type', 'application/json')
|
||||
self.write(dumps(data))
|
||||
|
||||
def put(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Handle PUT request.
|
||||
'''
|
||||
self.post()
|
||||
|
||||
|
||||
class ActionClassesHandler(RequestHandler): # pylint: disable=abstract-method
|
||||
'''
|
||||
Request handler which returns all action classes.
|
||||
'''
|
||||
|
||||
def initialize(self, core): # pylint: disable=arguments-differ
|
||||
'''
|
||||
Initialize request handler with Mopidy core.
|
||||
|
||||
:param mopidy.core.Core mopidy_core: The mopidy core instance
|
||||
'''
|
||||
self.core = core # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def get(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Handle GET request.
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<label for="parameter">Parameter</label>
|
||||
<input id="parameter" name="parameter" type="text" placeholder="A type-specific parameter">
|
||||
<button id="register-button" role="submit">✓ Register Tag</button>
|
||||
<button id="unregister-button" role="button">× Unregister Tag</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,15 +8,13 @@ class API {
|
|||
* Send AJAX request to REST API endpoint.
|
||||
*/
|
||||
|
||||
request(endpoint, data, callback)
|
||||
{
|
||||
request = (endpoint, data, callback) => {
|
||||
let init = {}
|
||||
if(data)
|
||||
init = { method: 'POST', body: data }
|
||||
|
||||
fetch(endpoint, init)
|
||||
.then(function(response)
|
||||
{
|
||||
.then((response) => {
|
||||
return response.json()
|
||||
})
|
||||
.then(callback)
|
||||
|
@ -27,24 +25,21 @@ class API {
|
|||
* Refresh the registry.
|
||||
*/
|
||||
|
||||
refreshRegistry()
|
||||
{
|
||||
let callback = function(response)
|
||||
{
|
||||
refreshRegistry = () => {
|
||||
let callback = (response) => {
|
||||
let tagsContainer = document.getElementById('tags')
|
||||
while(tagsContainer.firstChild)
|
||||
while(tagsContainer.firstChild) {
|
||||
tagsContainer.removeChild(tagsContainer.firstChild)
|
||||
}
|
||||
|
||||
for(let tag of response.tags)
|
||||
{
|
||||
for(let tag of response.tags) {
|
||||
let tagElement = document.createElement('div')
|
||||
tagElement.setAttribute('class', 'tag')
|
||||
|
||||
let args = new Array('alias', 'uid', 'action_class', 'parameter')
|
||||
for(let arg of args)
|
||||
{
|
||||
for(let arg of args) {
|
||||
let spanElement = document.createElement('span')
|
||||
let value = tag[arg] ? tag[arg] : '-'
|
||||
let value = tag[arg] ? tag[arg] : '-'
|
||||
spanElement.setAttribute('class', arg.replace('_', '-'))
|
||||
spanElement.innerHTML = value
|
||||
tagElement.appendChild(spanElement)
|
||||
|
@ -61,16 +56,13 @@ class API {
|
|||
* Refresh the tags.
|
||||
*/
|
||||
|
||||
refreshActionClasses()
|
||||
{
|
||||
let callback = function(response)
|
||||
{
|
||||
refreshActionClasses = () => {
|
||||
let callback = (response) => {
|
||||
let select = document.getElementById('action-class');
|
||||
while(select.firstChild)
|
||||
select.removeChild(select.firstChild)
|
||||
|
||||
for(let action_class in response.action_classes)
|
||||
{
|
||||
for(let action_class in response.action_classes) {
|
||||
let option = document.createElement('option')
|
||||
option.setAttribute('value', action_class)
|
||||
option.innerHTML = action_class + ' (' + response.action_classes[action_class] + ')'
|
||||
|
@ -81,45 +73,52 @@ class API {
|
|||
this.request('/pummeluff/action-classes/', false, callback)
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the form.
|
||||
*/
|
||||
|
||||
formCallback = (response) => {
|
||||
if(response.success) {
|
||||
this.refreshRegistry()
|
||||
document.getElementById('uid').value = ''
|
||||
document.getElementById('alias').value = ''
|
||||
document.getElementById('parameter').value = ''
|
||||
document.getElementById('action-class').selectIndex = 0
|
||||
} else {
|
||||
window.alert(response.message)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a new tag.
|
||||
*/
|
||||
|
||||
register()
|
||||
{
|
||||
register = () => {
|
||||
let form = document.getElementById('register-form')
|
||||
let data = new FormData(form)
|
||||
this.request('/pummeluff/register/', data, this.formCallback)
|
||||
}
|
||||
|
||||
let callback = function(response)
|
||||
{
|
||||
if(response.success)
|
||||
{
|
||||
api.refreshRegistry()
|
||||
document.getElementById('uid').value = ''
|
||||
document.getElementById('alias').value = ''
|
||||
document.getElementById('parameter').value = ''
|
||||
document.getElementById('action-class').selectIndex = 0
|
||||
}
|
||||
else
|
||||
{
|
||||
window.alert(response.message)
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Unregister an existing tag.
|
||||
*/
|
||||
|
||||
this.request('/pummeluff/register/', data, callback)
|
||||
unregister = () => {
|
||||
let form = document.getElementById('register-form')
|
||||
let data = new FormData(form)
|
||||
this.request('/pummeluff/unregister/', data, this.formCallback)
|
||||
}
|
||||
|
||||
/*
|
||||
* Get latest scanned tag.
|
||||
*/
|
||||
|
||||
getLatestTag()
|
||||
{
|
||||
getLatestTag = () => {
|
||||
let latest_tag = undefined
|
||||
|
||||
let uid_field = document.getElementById('uid')
|
||||
let alias_field = document.getElementById('alias')
|
||||
let parameter_field = document.getElementById('parameter')
|
||||
let uid_field = document.getElementById('uid')
|
||||
let alias_field = document.getElementById('alias')
|
||||
let parameter_field = document.getElementById('parameter')
|
||||
let action_class_select = document.getElementById('action-class')
|
||||
|
||||
uid_field.value = ''
|
||||
|
@ -127,15 +126,12 @@ class API {
|
|||
parameter_field.value = ''
|
||||
action_class_select.selectIndex = 0
|
||||
|
||||
let link = document.getElementById('read-rfid-tag')
|
||||
let link = document.getElementById('read-rfid-tag')
|
||||
link.classList.add('reading')
|
||||
|
||||
let do_request = function()
|
||||
{
|
||||
let callback = function(response)
|
||||
{
|
||||
if(latest_tag && response.success && JSON.stringify(response) != JSON.stringify(latest_tag))
|
||||
{
|
||||
let do_request = () => {
|
||||
let callback = (response) => {
|
||||
if(latest_tag && response.success && JSON.stringify(response) != JSON.stringify(latest_tag)) {
|
||||
uid_field.value = response.uid
|
||||
|
||||
if(response.alias)
|
||||
|
@ -148,9 +144,7 @@ class API {
|
|||
action_class_select.value = response.action_class
|
||||
|
||||
link.classList.remove('reading')
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
setTimeout(() => do_request(), 1000)
|
||||
}
|
||||
|
||||
|
@ -167,27 +161,28 @@ class API {
|
|||
|
||||
api = new API()
|
||||
|
||||
api.refreshRegistry();
|
||||
api.refreshActionClasses();
|
||||
api.refreshRegistry()
|
||||
api.refreshActionClasses()
|
||||
|
||||
document.addEventListener('click', function(event)
|
||||
{
|
||||
document.addEventListener('click', (event) => {
|
||||
let target = event.target
|
||||
let div = target.closest('div')
|
||||
|
||||
if(div && div.classList.contains('tag'))
|
||||
{
|
||||
for(let child of div.children)
|
||||
{
|
||||
if(div && div.classList.contains('tag')) {
|
||||
for(let child of div.children) {
|
||||
document.getElementById(child.className).value = child.innerHTML.replace(/^-$/, '')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
document.getElementById('register-form').onsubmit = function()
|
||||
{
|
||||
document.getElementById('register-form').onsubmit = () => {
|
||||
api.register()
|
||||
return false;
|
||||
}
|
||||
|
||||
document.getElementById('unregister-button').onclick = () => {
|
||||
api.unregister()
|
||||
return false
|
||||
}
|
||||
|
||||
document.getElementById('read-rfid-tag').onclick = () => api.getLatestTag()
|
||||
|
|
|
@ -18,11 +18,11 @@ body
|
|||
|
||||
body
|
||||
{
|
||||
min-height : 100vh;
|
||||
background-color: #222;
|
||||
color : #eee;
|
||||
font-family : sans-serif;
|
||||
font-size : 14px;
|
||||
color : #eee;
|
||||
background-color: #222;
|
||||
min-height : 100vh;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -80,15 +80,15 @@ input,
|
|||
select,
|
||||
button
|
||||
{
|
||||
width : 100%;
|
||||
border : 0;
|
||||
padding : 10px;
|
||||
border-width : 0 0 2px 0;
|
||||
border-style : solid;
|
||||
border-color : #333;
|
||||
background-color: #222;
|
||||
border : 0;
|
||||
border-color : #333;
|
||||
border-style : solid;
|
||||
border-width : 0 0 2px 0;
|
||||
color : #eee;
|
||||
outline : none;
|
||||
padding : 10px;
|
||||
width : 100%;
|
||||
}
|
||||
|
||||
select
|
||||
|
@ -103,26 +103,36 @@ input::placeholder
|
|||
|
||||
input:focus
|
||||
{
|
||||
border-bottom-color: #fa0;
|
||||
border-bottom-color: #8ff;
|
||||
}
|
||||
|
||||
button
|
||||
{
|
||||
color : #eee;
|
||||
cursor : pointer;
|
||||
font-weight: bold;
|
||||
margin-top : 10px;
|
||||
}
|
||||
|
||||
button#register-button
|
||||
{
|
||||
background-color: #4a4;
|
||||
color : #eee;
|
||||
font-weight : bold;
|
||||
margin-top : 10px;
|
||||
}
|
||||
|
||||
button#unregister-button
|
||||
{
|
||||
background-color: #a20;
|
||||
}
|
||||
|
||||
#read-rfid-tag
|
||||
{
|
||||
text-decoration: none;
|
||||
color : #fa0;
|
||||
color : #8ff;
|
||||
font-size : 11px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#read-rfid-tag.reading {
|
||||
animation: blink 0.5s cubic-bezier(.5, 0, 1, 1) infinite alternate;
|
||||
animation: blink 0.5s cubic-bezier(.5, 0, 1, 1) infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes blink { to { opacity: 0.25; } }
|
||||
|
@ -133,15 +143,15 @@ button
|
|||
|
||||
div.tag
|
||||
{
|
||||
display : inline-block;
|
||||
cursor : pointer;
|
||||
background-color: #eee;
|
||||
color : #222;
|
||||
box-shadow : 1px 1px 5px #000;
|
||||
padding : 10px;
|
||||
margin : 0 20px 20px 0;
|
||||
width : 400px;
|
||||
color : #222;
|
||||
cursor : pointer;
|
||||
display : inline-block;
|
||||
line-height : 20px;
|
||||
margin : 0 20px 20px 0;
|
||||
padding : 10px;
|
||||
width : 400px;
|
||||
}
|
||||
|
||||
div.tag span.uid,
|
||||
|
@ -153,14 +163,14 @@ div.tag span.parameter
|
|||
|
||||
div.tag span.action-class
|
||||
{
|
||||
display : inline-block;
|
||||
background-color: #888;
|
||||
border-radius : 10px;
|
||||
color : #eee;
|
||||
display : inline-block;
|
||||
font-size : 11px;
|
||||
line-height : 11px;
|
||||
padding : 3px 5px;
|
||||
margin-left : 5px;
|
||||
border-radius : 10px;
|
||||
padding : 3px 5px;
|
||||
}
|
||||
|
||||
div.tag span.alias,
|
||||
|
|
Loading…
Reference in a new issue