add support for lixie clock
This commit is contained in:
parent
dafd952a05
commit
e389a91ebd
29
events.py
29
events.py
|
@ -239,6 +239,35 @@ async def neopixel(request, ws, data):
|
||||||
# websocket response is handled under LightStrip.mqtt_callback
|
# websocket response is handled under LightStrip.mqtt_callback
|
||||||
|
|
||||||
|
|
||||||
|
async def lixie_clock(request, ws, data):
|
||||||
|
"""Changes the state of a Lixie Clock."""
|
||||||
|
device_id = data.get('device_id')
|
||||||
|
|
||||||
|
device = network.find(device_id)
|
||||||
|
if not device:
|
||||||
|
data['ok'] = False
|
||||||
|
data['error'] = "device_id not found"
|
||||||
|
await ws.send_json(data)
|
||||||
|
return
|
||||||
|
|
||||||
|
mqtt_msg = [device.mqtt_root]
|
||||||
|
payload = ''
|
||||||
|
if data.get('change_mode') == 'time':
|
||||||
|
mqtt_msg.append('time')
|
||||||
|
payload = ''
|
||||||
|
elif data.get('change_mode') == 'number':
|
||||||
|
mqtt_msg.append('number')
|
||||||
|
payload = data.get('display_number')
|
||||||
|
elif data.get('change_mode') == 'color':
|
||||||
|
mqtt_msg.append('color')
|
||||||
|
payload = tools.from_html_color(data.get('color'))
|
||||||
|
payload = str(payload)[1:-1].replace(' ', '')
|
||||||
|
mqtt_msg = '/'.join(mqtt_msg)
|
||||||
|
|
||||||
|
request.app['mqtt'].publish(mqtt_msg, payload)
|
||||||
|
# websocket response is handled under LixieClock.mqtt_callback
|
||||||
|
|
||||||
|
|
||||||
events = {}
|
events = {}
|
||||||
for obj in dir():
|
for obj in dir():
|
||||||
if type(locals()[obj]) == types.FunctionType:
|
if type(locals()[obj]) == types.FunctionType:
|
||||||
|
|
56
models.py
56
models.py
|
@ -68,6 +68,9 @@ class Device:
|
||||||
elif sub_device_dict.get('type') == 'NeoPixel':
|
elif sub_device_dict.get('type') == 'NeoPixel':
|
||||||
sub_device = NeoPixel().from_dict(sub_device_dict)
|
sub_device = NeoPixel().from_dict(sub_device_dict)
|
||||||
self.sub_devices.append(sub_device)
|
self.sub_devices.append(sub_device)
|
||||||
|
elif sub_device_dict.get('type') == 'LixieDisplay':
|
||||||
|
sub_device = LixieDisplay().from_dict(sub_device_dict)
|
||||||
|
self.sub_devices.append(sub_device)
|
||||||
else:
|
else:
|
||||||
typ = sub_device_dict.get('type')
|
typ = sub_device_dict.get('type')
|
||||||
raise ValueError(f"Unknown sub_device type {typ}")
|
raise ValueError(f"Unknown sub_device type {typ}")
|
||||||
|
@ -246,6 +249,56 @@ class NeoPixel(SubDevice):
|
||||||
self.color = [0,0,0] # JSON doesn't have tuples
|
self.color = [0,0,0] # JSON doesn't have tuples
|
||||||
|
|
||||||
|
|
||||||
|
class LixieClock(Device):
|
||||||
|
"""
|
||||||
|
Represents a clock made of Lixie displays.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.type = "LixieClock"
|
||||||
|
self.color = [0,0,0]
|
||||||
|
self.display_mode = 'time'
|
||||||
|
self.time_offset = -5
|
||||||
|
self.display_number = 0
|
||||||
|
|
||||||
|
async def mqtt_callback(self, app, msg):
|
||||||
|
topic = msg.topic.split('/')
|
||||||
|
payload = msg.payload.decode('utf8')
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
data['ok'] = True
|
||||||
|
data['device_id'] = self.id
|
||||||
|
|
||||||
|
if topic[1] == 'time':
|
||||||
|
data['change_mode'] = 'time'
|
||||||
|
self.display_mode = 'time'
|
||||||
|
elif topic[1] == 'number':
|
||||||
|
payload = int(payload)
|
||||||
|
data['change_mode'] = 'number'
|
||||||
|
data['display_number'] = payload
|
||||||
|
self.display_mode = 'number'
|
||||||
|
self.display_number = payload
|
||||||
|
elif topic[1] == 'color':
|
||||||
|
payload = payload.split(',')
|
||||||
|
payload = [int(num) for num in payload]
|
||||||
|
data['change_mode'] = 'color'
|
||||||
|
data['color'] = tools.to_html_color(payload)
|
||||||
|
self.color = payload
|
||||||
|
save_network(network)
|
||||||
|
|
||||||
|
res = {'event': 'lixie_clock', 'data': data}
|
||||||
|
await app.send_json_all(res)
|
||||||
|
|
||||||
|
|
||||||
|
class LixieDisplay(SubDevice):
|
||||||
|
"""
|
||||||
|
Represents a single Lixie display.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.id = ""
|
||||||
|
self.type = "LixieDisplay"
|
||||||
|
|
||||||
|
|
||||||
class DeviceEncoder(json.JSONEncoder):
|
class DeviceEncoder(json.JSONEncoder):
|
||||||
"""
|
"""
|
||||||
A custom json encoder for dumping devices to file.
|
A custom json encoder for dumping devices to file.
|
||||||
|
@ -269,6 +322,9 @@ def init_network(filepath="devices.json"):
|
||||||
elif device_dict.get('type') == 'LightStrip':
|
elif device_dict.get('type') == 'LightStrip':
|
||||||
device = LightStrip().from_dict(device_dict)
|
device = LightStrip().from_dict(device_dict)
|
||||||
network.append(device)
|
network.append(device)
|
||||||
|
elif device_dict.get('type') == 'LixieClock':
|
||||||
|
device = LixieClock().from_dict(device_dict)
|
||||||
|
network.append(device)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown device type {device_dict.get('type')}")
|
raise ValueError(f"Unknown device type {device_dict.get('type')}")
|
||||||
return network
|
return network
|
||||||
|
|
|
@ -44,6 +44,7 @@ function init_websocket() {
|
||||||
socket.events['lock_device'] = lock_device_recv;
|
socket.events['lock_device'] = lock_device_recv;
|
||||||
socket.events['delete_device'] = delete_device_recv;
|
socket.events['delete_device'] = delete_device_recv;
|
||||||
socket.events['neopixel'] = neopixel_recv;
|
socket.events['neopixel'] = neopixel_recv;
|
||||||
|
socket.events['lixie_clock'] = lixie_clock_recv;
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +228,22 @@ function neopixel_recv(data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Websocket send */
|
/* Websocket send */
|
||||||
|
@ -364,6 +380,23 @@ function neopixel_animation(device) {
|
||||||
socket.send('neopixel', data);
|
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
|
||||||
|
}
|
||||||
|
socket.send('lixie_clock', data);
|
||||||
|
}
|
||||||
|
|
||||||
/* DOM */
|
/* DOM */
|
||||||
function edit_field(field) {
|
function edit_field(field) {
|
||||||
let value = field.firstElementChild.innerText;
|
let value = field.firstElementChild.innerText;
|
||||||
|
@ -379,7 +412,7 @@ function edit_field(field) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function state_solid_amount(radio) {
|
function state_solid_amount(radio) {
|
||||||
let device = radio.parentElement.parentElement.parentElement.parentElement;
|
let device = radio.closest('.device');
|
||||||
let sub_devices = device.querySelector('.sub_devices');
|
let sub_devices = device.querySelector('.sub_devices');
|
||||||
for (let sub_device of sub_devices.children) {
|
for (let sub_device of sub_devices.children) {
|
||||||
let input = sub_device.firstElementChild.firstElementChild;
|
let input = sub_device.firstElementChild.firstElementChild;
|
||||||
|
@ -435,6 +468,18 @@ function animation_select(select, send_socket_event = true) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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';
|
||||||
|
} else if (radio.value === 'number') {
|
||||||
|
device.querySelector('.lixie_number').style.display = 'block';
|
||||||
|
}
|
||||||
|
if (send_socket_event) {
|
||||||
|
lixie_clock(radio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Misc */
|
/* Misc */
|
||||||
function get_object_from_svg(svg) {
|
function get_object_from_svg(svg) {
|
||||||
let all_objects = document.getElementsByTagName("object");
|
let all_objects = document.getElementsByTagName("object");
|
||||||
|
|
|
@ -131,18 +131,34 @@
|
||||||
<label for="animation_delay{{ device.id }}">ms</label>
|
<label for="animation_delay{{ device.id }}">ms</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% elif device.type == 'LixieClock' %}
|
||||||
|
<div class="mqtt_root editable"><span class="field_value">{{ device.mqtt_root }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)"></span>{% endif %}</div>
|
||||||
|
<div class="lixie_controls">
|
||||||
|
<input type="radio" id="display_mode_time_{{ device.id }}" class="time" name="display_mode_{{ device.id }}" value="time" onchange="lixie_display_mode(this)"{% if device.display_mode == 'time' %} checked{% endif %}>
|
||||||
|
<label for="display_mode_time_{{ device.id }}">Time</label><br>
|
||||||
|
<input type="radio" id="display_mode_number_{{ device.id }}" class="number" name="display_mode_{{ device.id }}" value="number" onchange="lixie_display_mode(this)"{% if device.display_mode == 'number' %} checked{% endif %}>
|
||||||
|
<label for="display_mode_number_{{ device.id }}">Number</label><br>
|
||||||
|
<div class='lixie_number' style="display: {% if device.display_mode == 'number' %}block{% else %}none{% endif %}">
|
||||||
|
<label for="lixie_number_{{ device.id }}">Number:</label>
|
||||||
|
<input type="number" id="lixie_number_{{ device.id }}" class="number" min="0" max="9999" value="{{ device.display_number }}" onchange="lixie_clock(this)">
|
||||||
|
</div>
|
||||||
|
<label for="display_color_{{ device.id }}">Color:</label>
|
||||||
|
<input type="color" id="display_color_{{ device.id }}" class="color" value="{{ device.color|html_color }}" onchange="lixie_clock(this)">
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="sub_devices">
|
<div class="sub_devices">
|
||||||
{% for sub_device in device.sub_devices %}
|
{% for sub_device in device.sub_devices %}
|
||||||
<div class="sub_device {{ sub_device.type }} {{ sub_device.id }}">
|
<div class="sub_device {{ sub_device.type }} {{ sub_device.id }}">
|
||||||
{% if device.type == 'RelayDevice' %}
|
{% if sub_device.type == 'RelayOutlet' %}
|
||||||
<div class="id">{{ sub_device.id }}</div>
|
<div class="id">{{ sub_device.id }}</div>
|
||||||
<object class="outlet_image" aria-label="Outlet Image" data="/static/outlet.svg"></object>
|
<object class="outlet_image" aria-label="Outlet Image" data="/static/outlet.svg"></object>
|
||||||
<div class="description editable"><span class="field_value">{{ sub_device.description }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)"></span>{% endif %}</div>
|
<div class="description editable"><span class="field_value">{{ sub_device.description }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)"></span>{% endif %}</div>
|
||||||
{% elif device.type == 'LightStrip' %}
|
{% elif sub_device.type == 'NeoPixel' %}
|
||||||
<label class="NeoPixel_color" style="background-color: {{ sub_device.color|html_color }}">
|
<label class="NeoPixel_color" style="background-color: {{ sub_device.color|html_color }}">
|
||||||
<input class="NeoPixel_color_input" type="color" value="{{ sub_device.color|html_color }}" onchange="neopixel_state(this.closest('.sub_device'))" disabled>
|
<input class="NeoPixel_color_input" type="color" value="{{ sub_device.color|html_color }}" onchange="neopixel_state(this.closest('.sub_device'))" disabled>
|
||||||
</label>
|
</label>
|
||||||
|
{% elif sub_device.type == 'LixieDisplay' %}
|
||||||
|
LixieDisplay
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user