527 lines
18 KiB
JavaScript
527 lines
18 KiB
JavaScript
var socket;
|
|
|
|
function load() {
|
|
socket = init_websocket();
|
|
|
|
// initialize RelayOutlet SVG color
|
|
Object.entries(init_state).forEach(([device_id, sub_devices]) => {
|
|
let device = document.querySelector('#' + device_id);
|
|
Object.entries(sub_devices).forEach(([sub_device_id, state]) => {
|
|
let sub_device = device.querySelector('.' + sub_device_id);
|
|
let svg = sub_device.querySelector('object').getSVGDocument().firstElementChild;
|
|
if (state) {
|
|
svg.classList.remove('off');
|
|
svg.classList.add('on');
|
|
}
|
|
});
|
|
});
|
|
|
|
/*document.querySelectorAll('input[type=color]').forEach((led_input) => {
|
|
led_input.onchange = function(event) {
|
|
}
|
|
});*/
|
|
}
|
|
|
|
/* Websocket setup */
|
|
function init_websocket() {
|
|
let socket = new WebSocket('wss://' + window.location.hostname + ws_uri);
|
|
socket._send = socket.send;
|
|
socket.send = function(event_title, data) {
|
|
data = JSON.stringify({event: event_title, data: data});
|
|
if (socket.readyState == 0) {
|
|
console.log("Socket is still opening!");
|
|
return;
|
|
}
|
|
socket._send(data);
|
|
}
|
|
socket.onmessage = onmessage;
|
|
socket.onclose = onclose;
|
|
socket.onerror = onerror;
|
|
socket.events = {};
|
|
socket.events['toggle_outlet'] = toggle_outlet_recv;
|
|
socket.events['edit_field'] = edit_field_recv;
|
|
socket.events['new_device'] = new_device_recv;
|
|
socket.events['lock_device'] = lock_device_recv;
|
|
socket.events['delete_device'] = delete_device_recv;
|
|
socket.events['neopixel'] = neopixel_recv;
|
|
socket.events['lixie_clock'] = lixie_clock_recv;
|
|
return socket;
|
|
}
|
|
|
|
function onmessage (e) {
|
|
let data = JSON.parse(e.data);
|
|
let event = data.event;
|
|
data = data.data;
|
|
if (!data.ok) {
|
|
throw new Error("Socket error: event = " + event + ", error = " + data.error);
|
|
}
|
|
if (socket.events[event] === undefined) {
|
|
console.log("Unknown socket event: " + event);
|
|
return;
|
|
}
|
|
socket.events[event](data);
|
|
}
|
|
|
|
async function onclose(e) {
|
|
if (e.wasClean) { return; } // no need to reconnect
|
|
console.log(e);
|
|
console.log('Websocket lost connection to server. Re-trying...');
|
|
socket = init_websocket();
|
|
await sleep(5000);
|
|
}
|
|
|
|
function onerror(e) {
|
|
console.log("Websocket error!")
|
|
console.log(e);
|
|
}
|
|
|
|
/* Websocket receive */
|
|
function toggle_outlet_recv(data) {
|
|
let device = document.querySelector('#' + data.device_id);
|
|
let sub_device = device.querySelector('.' + data.sub_device_id);
|
|
let svg = sub_device.querySelector('.outlet_image').getSVGDocument().firstChild;
|
|
if (data.state === true) {
|
|
svg.classList.remove('off');
|
|
svg.classList.add('on');
|
|
} else {
|
|
svg.classList.remove('on');
|
|
svg.classList.add('off');
|
|
}
|
|
}
|
|
|
|
function edit_field_recv(data) {
|
|
let device = document.querySelector('#' + data.device_id);
|
|
if (data.sub_device_id) {
|
|
device = device.querySelector('.' + data.sub_device_id);
|
|
}
|
|
let field = device.querySelector('.' + data.field);
|
|
|
|
let span = document.createElement('span');
|
|
span.innerText = data['value'];
|
|
span.className = 'field_value';
|
|
field.firstElementChild.replaceWith(span);
|
|
|
|
let edit = document.createElement('span');
|
|
edit.innerHTML = '';
|
|
edit.className = 'edit font-awesome';
|
|
edit.setAttribute('onclick', 'edit_field(this.parentElement)');
|
|
field.children[1].replaceWith(edit);
|
|
}
|
|
|
|
function new_device_recv(data) {
|
|
let device_type, sub_device_template;
|
|
if (data.device_type == 'relay_device') {
|
|
device_template = document.querySelector('#RelayDevice_template');
|
|
sub_device_template = document.querySelector('#RelayOutlet_template');
|
|
} else if (data.device_type == 'light_strip') {
|
|
device_template = document.querySelector('#LightStrip_template');
|
|
sub_device_template = document.querySelector('#NeoPixel_template');
|
|
} else if (data.device_type == 'lixie_clock') {
|
|
device_template = document.querySelector('#LixieClock_template');
|
|
sub_device_template = document.querySelector('#LixieDisplay_template');
|
|
}
|
|
|
|
let device = document.importNode(device_template.content, true);
|
|
device.querySelector('.id').querySelector('.field_value').textContent = data.device_id;
|
|
for (let sub_device_id of data.sub_device_ids) {
|
|
let sub_device = document.importNode(sub_device_template.content, true);
|
|
sub_device.firstElementChild.classList.add(sub_device_id);
|
|
if (sub_device.querySelector('.id')) {
|
|
sub_device.querySelector('.id').textContent = sub_device_id;
|
|
}
|
|
device.querySelector('.sub_devices').appendChild(sub_device);
|
|
}
|
|
|
|
document.querySelector('#devices').appendChild(device);
|
|
let children = document.querySelector('#devices').children;
|
|
children[children.length - 1].id = data.device_id;
|
|
}
|
|
|
|
function lock_device_recv(data) {
|
|
let device = document.querySelector('#' + data.device_id);
|
|
let fields = device.querySelectorAll('.editable');
|
|
|
|
if (data.locked) {
|
|
fields.forEach(function(field) {
|
|
field.querySelector('.edit').remove();
|
|
});
|
|
device.querySelector('.id').querySelector('.lock').remove();
|
|
device.querySelector('.id').querySelector('.delete').remove();
|
|
let unlock = document.createElement('span');
|
|
unlock.innerHTML = '';
|
|
unlock.className = 'unlock font-awesome';
|
|
unlock.setAttribute('onclick', 'unlock_device(this.parentElement.parentElement)');
|
|
device.querySelector('.id').appendChild(unlock);
|
|
} else {
|
|
fields.forEach(function(field) {
|
|
let edit = document.createElement('span');
|
|
edit.innerHTML = '';
|
|
edit.className = 'edit font-awesome';
|
|
edit.setAttribute('onclick', 'edit_field(this.parentElement)');
|
|
field.appendChild(edit);
|
|
});
|
|
device.querySelector('.id').querySelector('.unlock').remove();
|
|
|
|
let delete_elem = document.createElement('span');
|
|
delete_elem.innerHTML = '';
|
|
delete_elem.className = 'delete font-awesome';
|
|
delete_elem.setAttribute('onclick', 'delete_device(this.parentElement.parentElement)');
|
|
device.querySelector('.id').appendChild(delete_elem);
|
|
|
|
let lock = document.createElement('span');
|
|
lock.innerHTML = '';
|
|
lock.className = 'lock font-awesome';
|
|
lock.setAttribute('onclick', 'lock_device(this.parentElement.parentElement)');
|
|
device.querySelector('.id').appendChild(lock);
|
|
}
|
|
}
|
|
|
|
function delete_device_recv(data) {
|
|
let device = document.querySelector('#' + data.device_id);
|
|
device.remove()
|
|
}
|
|
|
|
function neopixel_recv(data) {
|
|
let device = document.querySelector('#' + data.device_id);
|
|
if (data.change_mode === 'state') {
|
|
select = device.querySelector('.state_select');
|
|
if (data.type === 'solid') {
|
|
select.value = data.type;
|
|
state_select(select, false);
|
|
if (data.amount === 'all') {
|
|
let sub_devices = device.querySelector('.sub_devices');
|
|
for (let sub_device of sub_devices.children) {
|
|
sub_device.firstElementChild.style.backgroundColor = data.color;
|
|
sub_device.firstElementChild.firstElementChild.value = data.color;
|
|
}
|
|
device.querySelector('#state_solid_all_color_' + device.id).value = data.color;
|
|
} else if (data.amount === 'single') {
|
|
let sub_device = device.querySelector('.' + data.sub_device_id);
|
|
sub_device.firstElementChild.style.backgroundColor = data.color;
|
|
sub_device.firstElementChild.firstElementChild.value = data.color;
|
|
}
|
|
} else if (data.type === 'rainbow') {
|
|
select.value = data.type;
|
|
state_select(select, false);
|
|
device.querySelector('#state_rainbow_red_freq_' + device.id).value = data.rainbow_params[0];
|
|
device.querySelector('#state_rainbow_green_freq_' + device.id).value = data.rainbow_params[1];
|
|
device.querySelector('#state_rainbow_blue_freq_' + device.id).value = data.rainbow_params[2];
|
|
device.querySelector('#state_rainbow_red_phase_' + device.id).value = data.rainbow_params[3];
|
|
device.querySelector('#state_rainbow_green_phase_' + device.id).value = data.rainbow_params[4];
|
|
device.querySelector('#state_rainbow_blue_phase_' + device.id).value = data.rainbow_params[5];
|
|
device.querySelector('#state_rainbow_center_' + device.id).value = data.rainbow_params[6];
|
|
device.querySelector('#state_rainbow_width_' + device.id).value = data.rainbow_params[7];
|
|
} else if (data.type === 'america') {
|
|
select.value = data.type;
|
|
state_select(select, false);
|
|
device.querySelector('#state_america_stripe_' + device.id).value = data.america_params[0];
|
|
device.querySelector('#state_america_magnitude_' + device.id).value = data.america_params[1];
|
|
}
|
|
} else if (data.change_mode === 'animation') {
|
|
if (data.property_type === 'mode') {
|
|
select = device.querySelector('.animation_select');
|
|
if (data.type === 'static') {
|
|
select.value = data.type;
|
|
animation_select(select, false);
|
|
} else if (data.type === 'rotate_left') {
|
|
select.value = data.type;
|
|
animation_select(select, false);
|
|
device.querySelector('#animation_rotate_count_' + device.id).value = data.rotate_count;
|
|
} else if (data.type === 'rotate_right') {
|
|
select.value = data.type;
|
|
animation_select(select, false);
|
|
device.querySelector('#animation_rotate_count_' + device.id).value = data.rotate_count;
|
|
}
|
|
} else if (data.property_type === 'delay') {
|
|
device.querySelector('#animation_delay_' + device.id).value = data.delay;
|
|
}
|
|
} else if (data.change_mode === 'strip') {
|
|
if (data.amount === 'full') {
|
|
let sub_devices = device.querySelector('.sub_devices');
|
|
for (let i = 0; i < sub_devices.children.length; i++) {
|
|
sub_devices.children[i].firstElementChild.style.backgroundColor = data.colors[i];
|
|
sub_devices.children[i].firstElementChild.firstElementChild.value = data.colors[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function lixie_clock_recv(data) {
|
|
let device = document.querySelector('#' + data.device_id);
|
|
if (data.change_mode === 'time') {
|
|
let radio = device.querySelector('#display_mode_time_' + device.id);
|
|
radio.checked = true;
|
|
lixie_display_mode(radio, false);
|
|
} else if (data.change_mode === 'number') {
|
|
let radio = device.querySelector('#display_mode_number_' + device.id);
|
|
radio.checked = true;
|
|
lixie_display_mode(radio, false);
|
|
device.querySelector('#lixie_number_' + device.id).value = data.display_number;
|
|
} else if (data.change_mode === 'color') {
|
|
device.querySelector('#display_color_' + device.id).value = data.color;
|
|
} else if (data.change_mode === 'time_offset') {
|
|
device.querySelector('#lixie_time_offset_' + device.id).value = data.time_offset;
|
|
}
|
|
}
|
|
|
|
/* Websocket send */
|
|
function toggle_outlet(svg) {
|
|
let sub_dev = get_object_from_svg(svg).parentElement;
|
|
let data = {
|
|
device_id: sub_dev.parentElement.parentElement.id,
|
|
sub_device_id: sub_dev.querySelector('.id').innerText,
|
|
};
|
|
socket.send('toggle_outlet', data);
|
|
}
|
|
|
|
function save_field(field) {
|
|
let value = field.firstElementChild.value;
|
|
let device_id = field.parentElement.id;
|
|
let sub_device_id;
|
|
if (field.parentElement.className.includes('sub_device')) {
|
|
sub_device_id = field.parentElement.children[0].textContent;
|
|
device_id = field.parentElement.parentElement.parentElement.id;
|
|
} else {
|
|
sub_device_id = '';
|
|
}
|
|
let data = {
|
|
device_id: device_id,
|
|
sub_device_id: sub_device_id,
|
|
field: field.classList[0],
|
|
value: value
|
|
};
|
|
socket.send('edit_field', data);
|
|
}
|
|
|
|
function new_device(dialog) {
|
|
let device_id = dialog.querySelector('#new_device_id').value;
|
|
let device_type = dialog.querySelector('input[name=new_device_type]:checked').value;
|
|
let num_sub_devices = dialog.querySelector('#new_device_num_sub_devices').value;
|
|
|
|
let data = {
|
|
'device_id': device_id,
|
|
'device_type': device_type,
|
|
'num_sub_devices': num_sub_devices,
|
|
}
|
|
socket.send('new_device', data);
|
|
}
|
|
|
|
function lock_device(device) {
|
|
if (device.querySelector('.save')) { return; }
|
|
let data = {
|
|
device_id: device.id,
|
|
locked: true,
|
|
};
|
|
socket.send('lock_device', data);
|
|
}
|
|
|
|
function unlock_device(device) {
|
|
let data = {
|
|
device_id: device.id,
|
|
locked: false,
|
|
};
|
|
socket.send('lock_device', data);
|
|
}
|
|
|
|
function delete_device(device) {
|
|
if (!window.confirm("Are you sure you want to delete this device?")) { return; }
|
|
let data = {
|
|
device_id: device.id,
|
|
};
|
|
socket.send('delete_device', data);
|
|
}
|
|
|
|
function neopixel_state(device) {
|
|
let sub_device;
|
|
if (device.classList.contains('sub_device')) {
|
|
sub_device = device;
|
|
device = sub_device.closest('.device');
|
|
}
|
|
let state_mode = device.querySelector('.state_select').value;
|
|
let data = {
|
|
device_id: device.id,
|
|
change_mode: 'state',
|
|
type: state_mode,
|
|
}
|
|
if (state_mode === 'solid') {
|
|
let amount;
|
|
let radios = device.querySelector('.state_solid').querySelectorAll('input[type=radio]');
|
|
for (let radio of radios) {
|
|
if (radio.checked) {
|
|
amount = radio.value;
|
|
break;
|
|
}
|
|
}
|
|
data['amount'] = amount;
|
|
if (amount === 'all') {
|
|
data['color'] = device.querySelector('#state_solid_all_color_' + device.id).value;
|
|
} else if (amount === 'single') {
|
|
data['color'] = sub_device.firstElementChild.firstElementChild.value;
|
|
data['sub_device_id'] = sub_device.classList[2];
|
|
}
|
|
} else if (state_mode === 'rainbow') {
|
|
data['rainbow_params'] = [
|
|
device.querySelector('#state_rainbow_red_freq_' + device.id).value,
|
|
device.querySelector('#state_rainbow_green_freq_' + device.id).value,
|
|
device.querySelector('#state_rainbow_blue_freq_' + device.id).value,
|
|
device.querySelector('#state_rainbow_red_phase_' + device.id).value,
|
|
device.querySelector('#state_rainbow_green_phase_' + device.id).value,
|
|
device.querySelector('#state_rainbow_blue_phase_' + device.id).value,
|
|
device.querySelector('#state_rainbow_center_' + device.id).value,
|
|
device.querySelector('#state_rainbow_width_' + device.id).value
|
|
]
|
|
} else if (state_mode === 'america') {
|
|
data['america_params'] = [
|
|
device.querySelector('#state_america_stripe_' + device.id).value,
|
|
device.querySelector('#state_america_magnitude_' + device.id).value
|
|
]
|
|
}
|
|
socket.send('neopixel', data);
|
|
}
|
|
|
|
function neopixel_animation(device) {
|
|
let property_type;
|
|
if (device.nodeName === 'INPUT') {
|
|
property_type = 'delay';
|
|
device = device.closest('.device');
|
|
} else {
|
|
property_type = 'mode';
|
|
}
|
|
let data = {
|
|
device_id: device.id,
|
|
change_mode: 'animation',
|
|
property_type: property_type
|
|
}
|
|
if (property_type === 'mode') {
|
|
let animation_mode = device.querySelector('.animation_select').value;
|
|
data['type'] = animation_mode;
|
|
if (animation_mode === 'static') {
|
|
} else if (animation_mode === 'rotate_left' || animation_mode === 'rotate_right') {
|
|
data['rotate_count'] = device.querySelector('#animation_rotate_count_' + device.id).value;
|
|
}
|
|
} else if (property_type === 'delay') {
|
|
data['delay'] = device.querySelector('#animation_delay_' + device.id).value;
|
|
}
|
|
socket.send('neopixel', data);
|
|
}
|
|
|
|
function lixie_clock(input) {
|
|
let device = input.closest('.device');
|
|
data = {
|
|
device_id: device.id
|
|
}
|
|
if (input.className === 'time') {
|
|
data['change_mode'] = 'time';
|
|
} else if (input.className === 'number') {
|
|
data['change_mode'] = 'number';
|
|
data['display_number'] = device.querySelector('#lixie_number_' + device.id).value;
|
|
} else if (input.className === 'color') {
|
|
data['change_mode'] = 'color';
|
|
data['color'] = input.value
|
|
} else if (input.className === 'time_offset') {
|
|
data['change_mode'] = 'time_offset';
|
|
data['time_offset'] = device.querySelector('#lixie_time_offset_' + device.id).value;
|
|
}
|
|
socket.send('lixie_clock', data);
|
|
}
|
|
|
|
/* DOM */
|
|
function edit_field(field) {
|
|
let value = field.firstElementChild.innerText;
|
|
let input = document.createElement('input');
|
|
input.value = value;
|
|
field.firstElementChild.replaceWith(input);
|
|
|
|
let save = document.createElement('span');
|
|
save.innerHTML = '';
|
|
save.className = 'save font-awesome';
|
|
save.setAttribute('onclick', 'save_field(this.parentElement)');
|
|
field.children[1].replaceWith(save);
|
|
}
|
|
|
|
function state_solid_amount(radio) {
|
|
let device = radio.closest('.device');
|
|
let sub_devices = device.querySelector('.sub_devices');
|
|
for (let sub_device of sub_devices.children) {
|
|
let input = sub_device.firstElementChild.firstElementChild;
|
|
if (radio.value === 'all') {
|
|
input.disabled = true;
|
|
} else if (radio.value === 'single') {
|
|
input.disabled = false;
|
|
}
|
|
}
|
|
if (radio.value === 'all') {
|
|
device.querySelector('#state_solid_all_color_' + device.id).disabled = false;
|
|
} else if (radio.value === 'single') {
|
|
device.querySelector('#state_solid_all_color_' + device.id).disabled = true;
|
|
}
|
|
}
|
|
|
|
function state_select(select, send_socket_event = true) {
|
|
let device = select.closest('.device');
|
|
if (select.value === 'solid') {
|
|
select.parentElement.querySelector('.state_solid').style.display = 'block';
|
|
select.parentElement.querySelector('.state_rainbow').style.display = 'none';
|
|
select.parentElement.querySelector('.state_america').style.display = 'none';
|
|
} else if (select.value === 'rainbow') {
|
|
select.parentElement.querySelector('.state_solid').style.display = 'none';
|
|
select.parentElement.querySelector('.state_rainbow').style.display = 'block';
|
|
select.parentElement.querySelector('.state_america').style.display = 'none';
|
|
} else if (select.value === 'america') {
|
|
select.parentElement.querySelector('.state_solid').style.display = 'none';
|
|
select.parentElement.querySelector('.state_rainbow').style.display = 'none';
|
|
select.parentElement.querySelector('.state_america').style.display = 'block';
|
|
}
|
|
let sub_devices = device.querySelector('.sub_devices');
|
|
for (let sub_device of sub_devices.children) {
|
|
let input = sub_device.firstElementChild.firstElementChild;
|
|
input.disabled = true;
|
|
}
|
|
if (send_socket_event) {
|
|
neopixel_state(device);
|
|
}
|
|
}
|
|
|
|
function animation_select(select, send_socket_event = true) {
|
|
let device = select.closest('.device');
|
|
if (select.value === 'static') {
|
|
select.parentElement.querySelector('.animation_static').style.display = 'block';
|
|
select.parentElement.querySelector('.animation_rotate').style.display = 'none';
|
|
} else if (select.value === 'rotate_left' || select.value === 'rotate_right') {
|
|
select.parentElement.querySelector('.animation_static').style.display = 'none';
|
|
select.parentElement.querySelector('.animation_rotate').style.display = 'block';
|
|
}
|
|
if (send_socket_event) {
|
|
neopixel_animation(device);
|
|
}
|
|
}
|
|
|
|
function lixie_display_mode(radio, send_socket_event = true) {
|
|
let device = radio.closest('.device');
|
|
if (radio.value === 'time') {
|
|
device.querySelector('.lixie_number').style.display = 'none';
|
|
device.querySelector('.lixie_time_offset').style.display = 'block';
|
|
} else if (radio.value === 'number') {
|
|
device.querySelector('.lixie_number').style.display = 'block';
|
|
device.querySelector('.lixie_time_offset').style.display = 'none';
|
|
}
|
|
if (send_socket_event) {
|
|
lixie_clock(radio);
|
|
}
|
|
}
|
|
|
|
/* Misc */
|
|
function get_object_from_svg(svg) {
|
|
let all_objects = document.getElementsByTagName("object");
|
|
for (let i=0; i < all_objects.length; i++) {
|
|
if (svg === all_objects[i].getSVGDocument().firstElementChild) {
|
|
return all_objects[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|