add support for lixie clock

This commit is contained in:
iou1name 2019-11-25 08:39:42 -05:00
parent dafd952a05
commit e389a91ebd
4 changed files with 149 additions and 3 deletions

View File

@ -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:

View File

@ -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

View File

@ -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");

View File

@ -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)">&#xe800;</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)">&#xe800;</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)">&#xe800;</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 %}