Merge pull request #14 from confirm/add-next-previous-actions

FEATURE: Add next & previous track actions
This commit is contained in:
Dominique Barton 2020-04-15 14:04:21 +02:00 committed by GitHub
commit 7590da3f44
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 419 additions and 373 deletions

View file

@ -62,9 +62,12 @@ Connecting the buttons (optional)
You can connect two buttons to the RPi:
- ``RPi pin 5`` - Power button: Shutdown the Raspberry Pi into halt state & wake it up again from halt state
- ``RPi pin 7`` - Playback button: Pause and resume the playback
- ``RPi pin 29`` - Playback button: Pause and resume the playback
- ``RPi pin 31`` - Stop button: Stops the playback
- ``RPi pin 33`` - Previous button: Changes to the previous track in the playlist
- ``RPi pin 35`` - Next button: Changes to the next track in the playlist
The buttons must shortcut their corresponding pins against ``GND`` (e.g. pin ``6``) when pressed. This means you want to connect one pin of the button (i.e. ``C``) to RPI's ``GND``, and the other one (i.e. ``NO``) to RPi's pin ``5`` or ``7``.
The buttons must shortcut their corresponding pins against ``GND`` (e.g. pin ``6``) when pressed. This means you want to connect one pin of the button (i.e. ``C``) to RPI's ``GND``, and the other one (i.e. ``NO``) to RPi's pin ``5``, ``29``, ``31``, ``33`` or ``35``.
Connecting the status LED (optional)
------------------------------------

View file

@ -7,7 +7,8 @@ import os
import mopidy
from .frontend import PummeluffFrontend
from .web import LatestHandler, RegistryHandler, RegisterHandler, TagClassesHandler
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}),
('/tag-classes/', TagClassesHandler, {'core': core}),
('/latest/', LatestHandler),
('/registry/', RegistryHandler),
('/register/', RegisterHandler),
('/unregister/', UnregisterHandler),
('/action-classes/', ActionClassesHandler),
]

View file

@ -1,88 +0,0 @@
'''
Python module for Mopidy Pummeluff actions.
'''
__all__ = (
'replace_tracklist',
'set_volume',
'play_pause',
'stop',
'shutdown',
)
from logging import getLogger
from os import system
LOGGER = getLogger(__name__)
def replace_tracklist(core, uri):
'''
Replace tracklist and play.
:param mopidy.core.Core core: The mopidy core instance
:param str uri: An URI for the tracklist replacement
'''
LOGGER.info('Replacing tracklist with URI "%s"', uri)
playlists = [playlist.uri for playlist in core.playlists.as_list().get()]
if uri in playlists:
uris = [item.uri for item in core.playlists.get_items(uri).get()]
else:
uris = [uri]
core.tracklist.clear()
core.tracklist.add(uris=uris)
core.playback.play()
def set_volume(core, volume):
'''
Set volume of the mixer.
:param mopidy.core.Core core: The mopidy core instance
:param volume: The new (percentage) volume
:type volume: int|str
'''
LOGGER.info('Setting volume to %s', volume)
try:
core.mixer.set_volume(int(volume))
except ValueError as ex:
LOGGER.error(str(ex))
def play_pause(core):
'''
Pause or resume the playback.
:param mopidy.core.Core core: The mopidy core instance
'''
playback = core.playback
if playback.get_state().get() == 'playing':
LOGGER.info('Pausing the playback')
playback.pause()
else:
LOGGER.info('Resuming the playback')
playback.resume()
def stop(core):
'''
Stop playback.
:param mopidy.core.Core core: The mopidy core instance
'''
LOGGER.info('Stopping playback')
core.playback.stop()
def shutdown(core): # pylint: disable=unused-argument
'''
Shutdown.
:param mopidy.core.Core core: The mopidy core instance
'''
LOGGER.info('Shutting down')
system('sudo /sbin/shutdown -h now')

View file

@ -3,19 +3,20 @@ Python module for Mopidy Pummeluff tags.
'''
__all__ = (
'Tracklist',
'Volume',
'PlayPause',
'Stop',
'PreviousTrack',
'NextTrack',
'Shutdown',
'Tracklist',
'Volume',
)
from .playback import PlayPause, Stop, PreviousTrack, NextTrack
from .shutdown import Shutdown
from .tracklist import Tracklist
from .volume import Volume
from .play_pause import PlayPause
from .stop import Stop
from .shutdown import Shutdown
TAGS = {}
for tag in __all__:
TAGS[tag] = globals()[tag].__doc__.strip()
ACTIONS = {}
for action in __all__:
ACTIONS[action] = globals()[action].__doc__.strip()

View file

@ -1,22 +1,36 @@
'''
Python module for Mopidy Pummeluff base tag.
Python module for Mopidy Pummeluff base action.
'''
__all__ = (
'Tag',
'Action',
)
from logging import getLogger
from inspect import getfullargspec
LOGGER = getLogger(__name__)
class Tag:
class Action:
'''
Base RFID tag class, which will implement the factory pattern in Python's
own :py:meth:`__new__` method.
'''
parameterised = True
@classmethod
def execute(cls, core):
'''
Execute the action.
:param mopidy.core.Core core: The mopidy core instance
:raises NotImplementedError: When class method is not implemented
'''
name = cls.__name__
error = 'Missing execute class method in the %s class'
LOGGER.error(error, name)
raise NotImplementedError(error % name)
def __init__(self, uid, alias=None, parameter=None):
'''
@ -56,21 +70,7 @@ class Tag:
args = [core]
if self.parameter:
args.append(self.parameter)
self.action.__func__(*args)
@property
def action(self):
'''
Return an action function defined in the
:py:mod:`mopidy_pummeluff.actions` Python module.
:return: An action
:raises NotImplementedError: When action property isn't defined
'''
cls = self.__class__.__name__
error = 'Missing action property in the %s class'
LOGGER.error(error, cls)
raise NotImplementedError(error % cls)
self.execute(*args)
def as_dict(self, include_scanned=False):
'''
@ -82,7 +82,7 @@ class Tag:
:rtype: dict
'''
data = {
'tag_class': self.__class__.__name__,
'action_class': self.__class__.__name__,
'uid': self.uid,
'alias': self.alias or '',
'parameter': self.parameter or '',
@ -99,8 +99,10 @@ class Tag:
:raises ValueError: When parameter is not allowed but defined
'''
if self.parameterised and not self.parameter:
parameterised = len(getfullargspec(self.execute).args) > 2
if parameterised and not self.parameter:
raise ValueError('Parameter required for this tag')
if not self.parameterised and self.parameter:
if not parameterised and self.parameter:
raise ValueError('No parameter allowed for this tag')

View file

@ -0,0 +1,86 @@
'''
Python module for Mopidy Pummeluff playback actions.
'''
__all__ = (
'PlayPause',
'Stop',
'PreviousTrack',
'NextTrack',
)
from logging import getLogger
from .base import Action
LOGGER = getLogger(__name__)
class PlayPause(Action):
'''
Pauses or resumes the playback, based on the current state.
'''
@classmethod
def execute(cls, core):
'''
Pause or resume the playback.
:param mopidy.core.Core core: The mopidy core instance
'''
playback = core.playback
if playback.get_state().get() == 'playing':
LOGGER.info('Pausing the playback')
playback.pause()
else:
LOGGER.info('Resuming the playback')
playback.resume()
class Stop(Action):
'''
Stops the playback.
'''
@classmethod
def execute(cls, core):
'''
Stop playback.
:param mopidy.core.Core core: The mopidy core instance
'''
LOGGER.info('Stopping playback')
core.playback.stop()
class PreviousTrack(Action):
'''
Changes to the previous track.
'''
@classmethod
def execute(cls, core):
'''
Change to previous track.
:param mopidy.core.Core core: The mopidy core instance
'''
LOGGER.info('Changing to previous track')
core.playback.previous()
class NextTrack(Action):
'''
Changes to the next track.
'''
@classmethod
def execute(cls, core):
'''
Change to next track.
:param mopidy.core.Core core: The mopidy core instance
'''
LOGGER.info('Changing to next track')
core.playback.next()

View file

@ -0,0 +1,30 @@
'''
Python module for Mopidy Pummeluff shutdown tag.
'''
__all__ = (
'Shutdown',
)
from logging import getLogger
from os import system
from .base import Action
LOGGER = getLogger(__name__)
class Shutdown(Action):
'''
Shutting down the system.
'''
@classmethod
def execute(cls, core):
'''
Shutdown.
:param mopidy.core.Core core: The mopidy core instance
'''
LOGGER.info('Shutting down')
system('sudo /sbin/shutdown -h now')

View file

@ -0,0 +1,41 @@
'''
Python module for Mopidy Pummeluff tracklist tag.
'''
__all__ = (
'Tracklist',
)
from logging import getLogger
from .base import Action
LOGGER = getLogger(__name__)
class Tracklist(Action):
'''
Replaces the current tracklist with the URI retreived from the tag's
parameter.
'''
@classmethod
def execute(cls, core, uri): # pylint: disable=arguments-differ
'''
Replace tracklist and play.
:param mopidy.core.Core core: The mopidy core instance
:param str uri: An URI for the tracklist replacement
'''
LOGGER.info('Replacing tracklist with URI "%s"', uri)
playlists = [playlist.uri for playlist in core.playlists.as_list().get()]
if uri in playlists:
uris = [item.uri for item in core.playlists.get_items(uri).get()]
else:
uris = [uri]
core.tracklist.clear()
core.tracklist.add(uris=uris)
core.playback.play()

View file

@ -6,16 +6,32 @@ __all__ = (
'Volume',
)
from mopidy_pummeluff.actions import set_volume
from .base import Tag
from logging import getLogger
from .base import Action
LOGGER = getLogger(__name__)
class Volume(Tag):
class Volume(Action):
'''
Sets the volume to the percentage value retreived from the tag's parameter.
'''
action = set_volume
@classmethod
def execute(cls, core, volume): # pylint: disable=arguments-differ
'''
Set volume of the mixer.
:param mopidy.core.Core core: The mopidy core instance
:param volume: The new (percentage) volume
:type volume: int|str
'''
LOGGER.info('Setting volume to %s', volume)
try:
core.mixer.set_volume(int(volume))
except ValueError as ex:
LOGGER.error(str(ex))
def validate(self):
'''

View file

@ -25,7 +25,7 @@ class PummeluffFrontend(pykka.ThreadingActor, mopidy_core.CoreListener):
'''
def __init__(self, config, core): # pylint: disable=unused-argument
super(PummeluffFrontend, self).__init__()
super().__init__()
self.core = core
self.stop_event = Event()
self.gpio_handler = GPIOHandler(core=core, stop_event=self.stop_event)

View file

@ -11,7 +11,7 @@ import os
import json
from logging import getLogger
from mopidy_pummeluff import tags
from mopidy_pummeluff import actions
LOGGER = getLogger(__name__)
@ -41,32 +41,35 @@ class RegistryDict(dict):
def unserialize_item(cls, item):
'''
Unserialize an item from the persistent storage on filesystem to a
native tag.
native action.
:param tuple item: The item
:return: The tag
:rtype: tags.tag
:return: The action
:rtype: actions.Action
'''
return item['uid'], cls.init_tag(**item)
if 'tag_class' in item:
item['action_class'] = item.pop('tag_class')
return item['uid'], cls.init_action(**item)
@classmethod
def init_tag(cls, tag_class, uid, alias=None, parameter=None):
def init_action(cls, action_class, uid, alias=None, parameter=None):
'''
Initialise a new tag instance.
Initialise a new action instance.
:param str tag_class: The tag class
:param str action_class: The action class
:param str uid: The RFID UID
:param str alias: The alias
:param str parameter: The parameter
:return: The tag instance
:rtype: tags.Tag
:return: The action instance
:rtype: actions.Action
'''
uid = str(uid).strip()
tag_class = getattr(tags, tag_class)
uid = str(uid).strip()
action_class = getattr(actions, action_class)
return tag_class(uid, alias, parameter)
return action_class(uid, alias, parameter)
def read(self):
'''
@ -94,35 +97,46 @@ class RegistryDict(dict):
os.makedirs(directory)
with open(config, 'w') as f:
json.dump([tag.as_dict() for tag in self.values()], f, indent=4)
json.dump([action.as_dict() for action in self.values()], f, indent=4)
def register(self, tag_class, uid, alias=None, parameter=None):
def register(self, action_class, uid, alias=None, parameter=None):
'''
Register a new tag in the registry.
:param str tag_class: The tag class
:param str action_class: The action class
:param str uid: The UID
:param str alias: The alias
:param str parameter: The parameter (optional)
:return: The tag
:rtype: tags.Tag
:return: The action
:rtype: actions.Action
'''
LOGGER.info('Registering %s tag %s with parameter "%s"', tag_class, uid, parameter)
LOGGER.info('Registering %s tag %s with parameter "%s"', action_class, uid, parameter)
tag = self.init_tag(
tag_class=tag_class,
action = self.init_action(
action_class=action_class,
uid=uid,
alias=alias,
parameter=parameter
)
tag.validate()
action.validate()
self[uid] = tag
self[uid] = action
self.write()
return tag
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()

View file

@ -1,18 +0,0 @@
'''
Python module for Mopidy Pummeluff play pause tag.
'''
__all__ = (
'PlayPause',
)
from mopidy_pummeluff.actions import play_pause
from .base import Tag
class PlayPause(Tag):
'''
Pauses or resumes the playback, based on the current state.
'''
action = play_pause
parameterised = False

View file

@ -1,18 +0,0 @@
'''
Python module for Mopidy Pummeluff shutdown tag.
'''
__all__ = (
'Shutdown',
)
from mopidy_pummeluff.actions import shutdown
from .base import Tag
class Shutdown(Tag):
'''
Shutting down the system.
'''
action = shutdown
parameterised = False

View file

@ -1,18 +0,0 @@
'''
Python module for Mopidy Pummeluff stop tag.
'''
__all__ = (
'Stop',
)
from mopidy_pummeluff.actions import stop
from .base import Tag
class Stop(Tag):
'''
Stops the playback.
'''
action = stop
parameterised = False

View file

@ -1,19 +0,0 @@
'''
Python module for Mopidy Pummeluff tracklist tag.
'''
__all__ = (
'Tracklist',
)
from mopidy_pummeluff.actions import replace_tracklist
from .base import Tag
class Tracklist(Tag):
'''
Replaces the current tracklist with the URI retreived from the tag's
parameter.
'''
action = replace_tracklist

View file

@ -12,7 +12,7 @@ from time import time
import RPi.GPIO as GPIO
from mopidy_pummeluff.actions import shutdown, play_pause
from mopidy_pummeluff.actions import Shutdown, PlayPause, Stop, PreviousTrack, NextTrack
from mopidy_pummeluff.sound import play_sound
LOGGER = getLogger(__name__)
@ -24,8 +24,11 @@ class GPIOHandler(Thread):
LED when it's started and then reacting to button presses.
'''
button_pins = {
5: shutdown,
7: play_pause,
5: Shutdown,
29: PlayPause,
31: Stop,
33: PreviousTrack,
35: NextTrack,
}
led_pin = 8
@ -76,5 +79,5 @@ class GPIOHandler(Thread):
if (GPIO.input(pin) == GPIO.LOW) and (now - before > 0.25):
LOGGER.debug('Button at pin %s was pushed', pin)
play_sound('success.wav')
self.button_pins[pin](self.core)
self.button_pins[pin].execute(self.core)
self.timestamps[pin] = now

View file

@ -14,7 +14,7 @@ import RPi.GPIO as GPIO
from pirc522 import RFID
from mopidy_pummeluff.registry import REGISTRY
from mopidy_pummeluff.tags.base import Tag
from mopidy_pummeluff.actions.base import Action
from mopidy_pummeluff.sound import play_sound
LOGGER = getLogger(__name__)
@ -104,15 +104,15 @@ class TagReader(Thread):
:param str uid: The UID
'''
try:
tag = REGISTRY[str(uid)]
action = REGISTRY[str(uid)]
LOGGER.info('Triggering action of registered tag')
play_sound('success.wav')
tag(self.core)
action(self.core)
except KeyError:
LOGGER.info('Tag is not registered, thus doing nothing')
play_sound('fail.wav')
tag = Tag(uid=uid)
action = Action(uid=uid)
tag.scanned = time()
TagReader.latest = tag
action.scanned = time()
TagReader.latest = action

View file

@ -6,7 +6,8 @@ __all__ = (
'LatestHandler',
'RegistryHandler',
'RegisterHandler',
'TagClassesHandler',
'UnregisterHandler',
'ActionClassesHandler',
)
from json import dumps
@ -15,7 +16,7 @@ from logging import getLogger
from tornado.web import RequestHandler
from mopidy_pummeluff.registry import REGISTRY
from mopidy_pummeluff.tags import TAGS
from mopidy_pummeluff.actions import ACTIONS
from mopidy_pummeluff.threads import TagReader
LOGGER = getLogger(__name__)
@ -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,21 +82,13 @@ 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.
'''
try:
tag = REGISTRY.register(
tag_class=self.get_argument('tag-class'),
action_class=self.get_argument('action-class'),
uid=self.get_argument('uid'),
alias=self.get_argument('alias', None),
parameter=self.get_argument('parameter', None),
@ -141,18 +118,44 @@ class RegisterHandler(RequestHandler): # pylint: disable=abstract-method
self.post()
class TagClassesHandler(RequestHandler): # pylint: disable=abstract-method
class UnregisterHandler(RequestHandler): # pylint: disable=abstract-method
'''
Request handler which returns all tag classes.
Request handler which unregisters an RFID tag from the registry.
'''
def initialize(self, core): # pylint: disable=arguments-differ
def post(self, *args, **kwargs): # pylint: disable=unused-argument
'''
Initialize request handler with Mopidy core.
Handle POST request.
'''
try:
REGISTRY.unregister(uid=self.get_argument('uid'))
:param mopidy.core.Core mopidy_core: The mopidy core instance
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
'''
self.core = core # pylint: disable=attribute-defined-outside-init
Handle PUT request.
'''
self.post()
class ActionClassesHandler(RequestHandler): # pylint: disable=abstract-method
'''
Request handler which returns all action classes.
'''
def get(self, *args, **kwargs): # pylint: disable=unused-argument
'''
@ -160,8 +163,8 @@ class TagClassesHandler(RequestHandler): # pylint: disable=abstract-method
'''
data = {
'success': True,
'message': 'Tag classes successfully retreived',
'tag_classes': TAGS
'message': 'Action classes successfully retreived',
'action_classes': ACTIONS
}
self.set_header('Content-type', 'application/json')

View file

@ -21,12 +21,13 @@
<a id="read-rfid-tag" href="#">Read UID from RFID tag…</a>
<label for="alias">Alias</label>
<input id="alias" name="alias" type="text" placeholder="Your personal alias / identifier">
<label for="tag-class">Class</label>
<select id="tag-class" name="tag-class">
<label for="action-class">Class</label>
<select id="action-class" name="action-class">
</select>
<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>

View file

@ -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', 'tag_class', 'parameter')
for(let arg of args)
{
let args = new Array('alias', 'uid', 'action_class', 'parameter')
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,81 +56,82 @@ class API {
* Refresh the tags.
*/
refreshTagClasses()
{
let callback = function(response)
{
let select = document.getElementById('tag-class');
refreshActionClasses = () => {
let callback = (response) => {
let select = document.getElementById('action-class');
while(select.firstChild)
select.removeChild(select.firstChild)
for(let tag_class in response.tag_classes)
{
for(let action_class in response.action_classes) {
let option = document.createElement('option')
option.setAttribute('value', tag_class)
option.innerHTML = tag_class + ' (' + response.tag_classes[tag_class] + ')'
option.setAttribute('value', action_class)
option.innerHTML = action_class + ' (' + response.action_classes[action_class] + ')'
select.appendChild(option)
}
}
this.request('/pummeluff/tag-classes/', false, callback)
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('tag-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 tag_class_select = document.getElementById('tag-class')
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 = ''
alias_field.value = ''
parameter_field.value = ''
tag_class_select.selectIndex = 0
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)
@ -144,13 +140,11 @@ class API {
if(response.parameter)
parameter_field.value = response.parameter
if(response.tag_class)
tag_class_select.value = response.tag_class
if(response.action_class)
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.refreshTagClasses();
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()

View file

@ -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,34 +143,34 @@ 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,
div.tag span.tag-class,
div.tag span.action-class,
div.tag span.parameter
{
font-family: Courier New, monospace;
}
div.tag span.tag-class
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,