fix(ragb): hotfix buttons
This commit is contained in:
parent
700d967ff3
commit
ddfbbe50e3
13 changed files with 277 additions and 18 deletions
|
@ -49,7 +49,7 @@ pub async fn batch_edit_value_handler(
|
|||
lock.dmx.colors[i.address] = i.value;
|
||||
match db.static_state.change_channel.send(DMXAtom::Color(*i)) {
|
||||
Ok(_) => (),
|
||||
Err(_) => (),
|
||||
Err(e) => tracing::error!("Channel error: {e:?}"),
|
||||
}
|
||||
}
|
||||
lock.ratelimit_info.insert(user, Instant::now());
|
||||
|
@ -63,7 +63,11 @@ pub async fn get_value_handler(
|
|||
) -> Result<impl IntoResponse, StatusCode> {
|
||||
let lock = db.mut_state.read().await;
|
||||
check_id(id, &lock.dmx.colors)?;
|
||||
Ok(Json(lock.dmx.colors[id]))
|
||||
if lock.dmx.motor.black_button {
|
||||
Ok(Json(lock.dmx.motor.bump_color))
|
||||
} else {
|
||||
Ok(Json(lock.dmx.colors[id]))
|
||||
}
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
|
@ -72,6 +76,7 @@ pub async fn edit_value_handler(
|
|||
State(db): State<DB>,
|
||||
Json(body): Json<DMXColor>,
|
||||
) -> Result<(), StatusCode> {
|
||||
tracing::trace!("Edit color {body:?}");
|
||||
let mut lock = db.mut_state.write().await;
|
||||
check_id(id, &lock.dmx.colors)?;
|
||||
lock.dmx.colors[id] = body;
|
||||
|
@ -81,7 +86,7 @@ pub async fn edit_value_handler(
|
|||
.send(DMXAtom::Color(DMXColorAtom::new(id, body)))
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(_) => (),
|
||||
Err(e) => tracing::error!("Channel error: {e:?}"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
@ -101,15 +106,45 @@ pub async fn edit_motor_value_handler(
|
|||
Json(body): Json<DMXBeamChange>,
|
||||
) -> Result<(), StatusCode> {
|
||||
let mut lock = db.mut_state.write().await;
|
||||
tracing::trace!("Edit motor {body:?}");
|
||||
if let Some(new) = body.black_button {
|
||||
if new != lock.dmx.motor.black_button {
|
||||
for (i, x) in lock
|
||||
.dmx
|
||||
.colors
|
||||
.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
match db
|
||||
.static_state
|
||||
.change_channel
|
||||
.send(DMXAtom::Color(DMXColorAtom {
|
||||
address: i,
|
||||
value: if new { lock.dmx.motor.bump_color } else { x },
|
||||
})) {
|
||||
Ok(_) => (),
|
||||
Err(e) => tracing::error!("Channel error: {e:?}"),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
lock.dmx.motor = DMXBeam {
|
||||
pan: body.pan.unwrap_or(lock.dmx.motor.pan),
|
||||
tilt: body.tilt.unwrap_or(lock.dmx.motor.tilt),
|
||||
focus: body.focus.unwrap_or(lock.dmx.motor.focus),
|
||||
white_button: body.white_button.unwrap_or(lock.dmx.motor.white_button),
|
||||
black_button: body.black_button.unwrap_or(lock.dmx.motor.black_button),
|
||||
bump_color: body.bump_color.unwrap_or(lock.dmx.motor.bump_color),
|
||||
};
|
||||
let _ = db
|
||||
match db
|
||||
.static_state
|
||||
.change_channel
|
||||
.send(DMXAtom::Motor(lock.dmx.motor));
|
||||
.send(DMXAtom::Motor(lock.dmx.motor))
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(e) => tracing::error!("Channel error: {e:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -127,10 +162,14 @@ pub async fn sse_handler(State(db): State<DB>) -> impl IntoResponse {
|
|||
let init_data = data
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| {
|
||||
.map(move |(i, x)| {
|
||||
Ok(DMXAtom::Color(DMXColorAtom {
|
||||
address: i,
|
||||
value: x,
|
||||
value: if motor.black_button {
|
||||
motor.bump_color
|
||||
} else {
|
||||
x
|
||||
},
|
||||
}))
|
||||
})
|
||||
.chain(std::iter::once(Ok(DMXAtom::Motor(motor))));
|
||||
|
|
|
@ -22,6 +22,9 @@ pub struct DMXBeam {
|
|||
pub pan: u8,
|
||||
pub tilt: u8,
|
||||
pub focus: u8,
|
||||
pub white_button: u8,
|
||||
pub black_button: bool,
|
||||
pub bump_color: DMXColor,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)]
|
||||
|
@ -29,6 +32,9 @@ pub struct DMXBeamChange {
|
|||
pub pan: Option<u8>,
|
||||
pub tilt: Option<u8>,
|
||||
pub focus: Option<u8>,
|
||||
pub white_button: Option<u8>,
|
||||
pub black_button: Option<bool>,
|
||||
pub bump_color: Option<DMXColor>,
|
||||
}
|
||||
|
||||
pub type ColorArray = Vec<DMXColor>;
|
||||
|
@ -114,7 +120,7 @@ pub type DB = Arc<SharedState>;
|
|||
|
||||
pub fn make_db() -> DB {
|
||||
let state_size: usize =
|
||||
usize::from_str(&dotenvy::var("NB_DMX_VALUES").unwrap_or(String::from("100"))).unwrap();
|
||||
usize::from_str(&dotenvy::var("NB_DMX_VALUES").unwrap_or(String::from("130"))).unwrap();
|
||||
let save_path = dotenvy::var("BK_FILE").unwrap_or(String::from("data.json"));
|
||||
let mut_state = RwLock::new(AppState::new(state_size, &save_path));
|
||||
Arc::new(SharedState {
|
||||
|
|
20
frontend/frontend/mixins.py
Normal file
20
frontend/frontend/mixins.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
|
||||
class GroupRequiredMixin(object):
|
||||
"""
|
||||
group_required - list of strings, required param
|
||||
"""
|
||||
|
||||
group_required = None
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
raise PermissionDenied
|
||||
else:
|
||||
user_groups = []
|
||||
for group in request.user.groups.values_list("name", flat=True):
|
||||
user_groups.append(group)
|
||||
if len(set(user_groups).intersection(self.group_required)) <= 0:
|
||||
raise PermissionDenied
|
||||
return super(GroupRequiredMixin, self).dispatch(request, *args, **kwargs)
|
|
@ -21,13 +21,23 @@ let startSocket = () => {
|
|||
|
||||
socket.addEventListener("message", (event) => {
|
||||
const message = JSON.parse(event.data);
|
||||
const color = rgbToHex(message.value);
|
||||
console.log(color, `light${message.address}`);
|
||||
const element = document.getElementById(`light${message.address}`);
|
||||
let color;
|
||||
let address;
|
||||
if(message.type === "Motor") {
|
||||
console.log(message);
|
||||
color = rgbToHex(message.bump_color);
|
||||
address = 300;
|
||||
|
||||
} else {
|
||||
color = rgbToHex(message.value);
|
||||
address = message.address;
|
||||
}
|
||||
const element = document.getElementById(`light${address}`);
|
||||
const inputElement = document.getElementById(`input-light${address}`);
|
||||
console.log(color, `light${address}`);
|
||||
if(element !== null) {
|
||||
element.style.fill = color;
|
||||
}
|
||||
const inputElement = document.getElementById(`input-light${message.address}`);
|
||||
if(inputElement !== null) {
|
||||
inputElement.value = color;
|
||||
}
|
||||
|
@ -113,4 +123,36 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Add a click event to save colors
|
||||
(document.querySelectorAll('.save-bump-color') || []).forEach(($trigger) => {
|
||||
const lightId = $trigger.dataset.target;
|
||||
const $input = document.getElementById(`input-light${lightId}`);
|
||||
const url = `${WEBSOCKET_ENDPOINT}/control-box`;
|
||||
|
||||
$trigger.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
const color = hexToRgb($input.value);
|
||||
openModal($loadingModal)
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
//mode: "no-cors",
|
||||
headers: {
|
||||
'Accept': "application/json",
|
||||
'Content-Type': "application/json",
|
||||
'Authorization': `Bearer ${JWT}`
|
||||
},
|
||||
body: JSON.stringify({ "bump_color": {"red": color.r, "green": color.g, "blue": color.b}}),
|
||||
}).then((resp) => {
|
||||
if(!resp.ok) {
|
||||
alert(`Request failed. Err code:${resp.status}`);
|
||||
}
|
||||
}).catch((e) => {
|
||||
alert(`Request failed. Err: ${e}`);
|
||||
console.log(e);
|
||||
}).finally(() => {
|
||||
closeAllModals();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% load static %}
|
||||
{% load has_group %}
|
||||
<!doctype html>
|
||||
<html data-theme="light" lang="en">
|
||||
<head>
|
||||
|
@ -27,6 +28,11 @@
|
|||
<li class="hide-before-sm">
|
||||
<a class="contrast" href="/api-docs/">API</a>
|
||||
</li>
|
||||
{% if user|has_group:"cof" %}
|
||||
<li class="hide-before-sm">
|
||||
<a class="contrast" href="{% url "frontend:bump" %}">Cof only</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if user.is_staff %}
|
||||
<li class="hide-before-sm">
|
||||
<a class="contrast" href="{% url "admin:index" %}">Admin</a>
|
||||
|
|
17
frontend/frontend/templates/frontend/bump.html
Normal file
17
frontend/frontend/templates/frontend/bump.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "frontend/base.html" %}
|
||||
{% load static %}
|
||||
{% block content %}
|
||||
<section>
|
||||
<h1>Couleur du bump</h1>
|
||||
<form>
|
||||
<input id="input-light300" class="input" type="color" name="light300">
|
||||
<button class="save-bump-color" data-target="300">Enregistrer</button>
|
||||
</form>
|
||||
</section>
|
||||
<dialog open id="modal-loading">
|
||||
<article aria-busy="true"></article>
|
||||
</dialog>
|
||||
<script>const WEBSOCKET_ENDPOINT = "{{ websocket_endpoint }}";</script>
|
||||
<script>const JWT = "{{ jwt }}";</script>
|
||||
<script src="{% static "js/update-color.js" %}" defer></script>
|
||||
{% endblock %}
|
|
@ -1877,7 +1877,7 @@
|
|||
xlink:href="/light/blinder">
|
||||
<g
|
||||
id="g5280"
|
||||
transform="matrix(0.22165795,0,0,0.22165795,16.4358,15.843397)">
|
||||
transform="matrix(0.22165795,0,0,0.22165795,46.4358,25.843397)">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.909091;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
id="rect374"
|
||||
|
@ -1984,6 +1984,50 @@
|
|||
r="4.5454545" />
|
||||
</g>
|
||||
</a>
|
||||
<a
|
||||
id="a6346"
|
||||
xlink:href="/light/spot_pont_0"
|
||||
style="fill-opacity:1">
|
||||
<circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="light{{lights.spot_pont_0.channels.0}}"
|
||||
cx="25.063156"
|
||||
cy="30.8321233"
|
||||
r="2.4541624" />
|
||||
</a>
|
||||
<a
|
||||
id="a6346"
|
||||
xlink:href="/light/spot_pont_3"
|
||||
style="fill-opacity:1">
|
||||
<circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="light{{lights.spot_pont_3.channels.0}}"
|
||||
cx="80.063156"
|
||||
cy="30.8321233"
|
||||
r="2.4541624" />
|
||||
</a>
|
||||
<a
|
||||
id="a6346"
|
||||
xlink:href="/light/spot_pont_1"
|
||||
style="fill-opacity:1">
|
||||
<circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="light{{lights.spot_pont_1.channels.0}}"
|
||||
cx="43.063156"
|
||||
cy="32.8321233"
|
||||
r="2.4541624" />
|
||||
</a>
|
||||
<a
|
||||
id="a6346"
|
||||
xlink:href="/light/spot_pont_2"
|
||||
style="fill-opacity:1">
|
||||
<circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="light{{lights.spot_pont_2.channels.0}}"
|
||||
cx="61.063156"
|
||||
cy="32.8321233"
|
||||
r="2.4541624" />
|
||||
</a>
|
||||
<a
|
||||
id="a6346"
|
||||
xlink:href="/light/screen_right"
|
||||
|
@ -2037,6 +2081,28 @@
|
|||
y="3.4841893"
|
||||
transform="rotate(16.718917)" />
|
||||
</a>
|
||||
<a
|
||||
id="a6260"
|
||||
xlink:href="/light/barre_led_4">
|
||||
<rect
|
||||
style="fill:url(#linearGradient6214);fill-opacity:1;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
width="13.756657"
|
||||
height="1.6411031"
|
||||
x="62.915016"
|
||||
y="28.4841893"
|
||||
transform="rotate(0)" />
|
||||
</a>
|
||||
<a
|
||||
id="a6260"
|
||||
xlink:href="/light/barre_led_5">
|
||||
<rect
|
||||
style="fill:url(#linearGradient6135);fill-opacity:1;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
width="13.756657"
|
||||
height="1.6411031"
|
||||
x="27.915016"
|
||||
y="28.4841893"
|
||||
transform="rotate(0)" />
|
||||
</a>
|
||||
<a
|
||||
id="a6340"
|
||||
xlink:href="/light/barre_led_1">
|
||||
|
|
0
frontend/frontend/templatetags/__init__.py
Normal file
0
frontend/frontend/templatetags/__init__.py
Normal file
8
frontend/frontend/templatetags/has_group.py
Normal file
8
frontend/frontend/templatetags/has_group.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(name="has_group")
|
||||
def has_group(user, group_name):
|
||||
return user.groups.filter(name=group_name).exists()
|
|
@ -1,10 +1,11 @@
|
|||
from django.urls import path
|
||||
|
||||
from .views import HomeView, LightView, TokenView
|
||||
from .views import BumpView, HomeView, LightView, TokenView
|
||||
|
||||
app_name = "frontend"
|
||||
urlpatterns = [
|
||||
path("", HomeView.as_view(), name="home"),
|
||||
path("light/<str:light>/", LightView.as_view()),
|
||||
path("bump", BumpView.as_view(), name="bump"),
|
||||
path("token", TokenView.as_view()),
|
||||
]
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.http import Http404, JsonResponse
|
|||
from django.views import View
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from .mixins import GroupRequiredMixin
|
||||
from .utils import craft_token
|
||||
|
||||
|
||||
|
@ -39,10 +40,15 @@ def get_context_from_proj(kind, chans):
|
|||
|
||||
class TokenView(LoginRequiredMixin, View):
|
||||
def get(self, request, *arg, **kwargs):
|
||||
return JsonResponse(craft_token(self.request.user.username, self.request.user.groups.filter(name="cof").exists()))
|
||||
return JsonResponse(
|
||||
craft_token(
|
||||
self.request.user.username,
|
||||
self.request.user.groups.filter(name="cof").exists(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class LightView(TemplateView):
|
||||
class LightView(LoginRequiredMixin, TemplateView):
|
||||
def get_template_names(self):
|
||||
lights = settings.LIGHTS["lights"][self.kwargs["light"]]
|
||||
return [f"frontend/{lights['kind']}.html"]
|
||||
|
@ -50,7 +56,10 @@ class LightView(TemplateView):
|
|||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
if self.request.user.is_authenticated:
|
||||
context["jwt"] = craft_token(self.request.user.username, self.request.user.groups.filter(name="cof").exists())["token"]
|
||||
context["jwt"] = craft_token(
|
||||
self.request.user.username,
|
||||
self.request.user.groups.filter(name="cof").exists(),
|
||||
)["token"]
|
||||
context["websocket_endpoint"] = settings.WEBSOCKET_ENDPOINT
|
||||
light = self.kwargs["light"]
|
||||
if light not in settings.LIGHTS["lights"]:
|
||||
|
@ -62,6 +71,21 @@ class LightView(TemplateView):
|
|||
return context
|
||||
|
||||
|
||||
class BumpView(GroupRequiredMixin, TemplateView):
|
||||
template_name = "frontend/bump.html"
|
||||
group_required = ["cof"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
if self.request.user.is_authenticated:
|
||||
context["jwt"] = craft_token(
|
||||
self.request.user.username,
|
||||
self.request.user.groups.filter(name="cof").exists(),
|
||||
)["token"]
|
||||
context["websocket_endpoint"] = settings.WEBSOCKET_ENDPOINT
|
||||
return context
|
||||
|
||||
|
||||
class HomeView(TemplateView):
|
||||
template_name = "frontend/home.html"
|
||||
|
||||
|
|
|
@ -44,6 +44,36 @@
|
|||
"name": "Barre led 4",
|
||||
"kind": "led_tub",
|
||||
"channels" : [68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83]
|
||||
},
|
||||
"barre_led_4": {
|
||||
"name": "Barre led 5",
|
||||
"kind": "led_tub",
|
||||
"channels" : [84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
|
||||
},
|
||||
"barre_led_5": {
|
||||
"name": "Barre led 6",
|
||||
"kind": "led_tub",
|
||||
"channels" : [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115]
|
||||
},
|
||||
"spot_pont_0": {
|
||||
"name": "Ecran-gauche",
|
||||
"kind": "spot",
|
||||
"channels": [ 116 ]
|
||||
},
|
||||
"spot_pont_1": {
|
||||
"name": "Ecran-gauche",
|
||||
"kind": "spot",
|
||||
"channels": [ 117 ]
|
||||
},
|
||||
"spot_pont_2": {
|
||||
"name": "Ecran-gauche",
|
||||
"kind": "spot",
|
||||
"channels": [ 118 ]
|
||||
},
|
||||
"spot_pont_3": {
|
||||
"name": "Ecran-gauche",
|
||||
"kind": "spot",
|
||||
"channels": [ 119 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ DEV_KEY = "supersecret"
|
|||
|
||||
WEBSOCKET_PORT = 9999
|
||||
WEBSOCKET_HOST = "127.0.0.1"
|
||||
WEBSOCKET_ENDPOINT = f"http://{WEBSOCKET_HOST}:{WEBSOCKET_PORT}/api"
|
||||
WEBSOCKET_ENDPOINT = "https://agb.hackens.org/api"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
|
Loading…
Reference in a new issue