vue.js refactor part 1 - prepared webpanel logic and migrated light.hue plugin
This commit is contained in:
parent
f053aa455d
commit
e72d4de581
49 changed files with 1666 additions and 393 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,3 +12,4 @@ platypush/backend/http/static/resources/*
|
||||||
docs/build
|
docs/build
|
||||||
.idea/
|
.idea/
|
||||||
config
|
config
|
||||||
|
platypush/backend/http/static/css/*/.sass-cache/
|
||||||
|
|
|
@ -24,29 +24,39 @@ def index():
|
||||||
if not authentication_ok(request):
|
if not authentication_ok(request):
|
||||||
return authenticate()
|
return authenticate()
|
||||||
|
|
||||||
# These plugins have their own template file but won't be shown as a tab in
|
|
||||||
# the web panel. This is usually the case for plugins that only include JS
|
|
||||||
# code but no template content.
|
|
||||||
_hidden_plugins = {
|
|
||||||
'assistant.google'
|
|
||||||
}
|
|
||||||
|
|
||||||
configured_plugins = Config.get_plugins()
|
configured_plugins = Config.get_plugins()
|
||||||
enabled_plugins = {}
|
enabled_templates = {}
|
||||||
hidden_plugins = {}
|
enabled_scripts = {}
|
||||||
|
enabled_styles = {}
|
||||||
|
|
||||||
|
js_folder = os.path.abspath(
|
||||||
|
os.path.join(template_folder, '..', 'static', 'js'))
|
||||||
|
style_folder = os.path.abspath(
|
||||||
|
os.path.join(template_folder, '..', 'static', 'css', 'dist'))
|
||||||
|
|
||||||
for plugin, conf in configured_plugins.items():
|
for plugin, conf in configured_plugins.items():
|
||||||
template_file = os.path.join('panel', plugin, 'index.html')
|
template_file = os.path.join(
|
||||||
if os.path.isfile(os.path.join(template_folder, template_file)):
|
template_folder, 'plugins', plugin, 'index.html')
|
||||||
if plugin in _hidden_plugins:
|
|
||||||
hidden_plugins[plugin] = conf
|
script_file = os.path.join(js_folder, 'plugins', plugin, 'index.js')
|
||||||
else:
|
style_file = os.path.join(style_folder, 'webpanel', 'plugins', plugin+'.css')
|
||||||
enabled_plugins[plugin] = conf
|
|
||||||
|
if os.path.isfile(template_file):
|
||||||
|
conf['_template_file'] = '/' + '/'.join(template_file.split(os.sep)[-3:])
|
||||||
|
enabled_templates[plugin] = conf
|
||||||
|
|
||||||
|
if os.path.isfile(script_file):
|
||||||
|
conf['_script_file'] = '/'.join(script_file.split(os.sep)[-4:])
|
||||||
|
enabled_scripts[plugin] = conf
|
||||||
|
|
||||||
|
if os.path.isfile(style_file):
|
||||||
|
conf['_style_file'] = 'css/dist/' + style_file[len(style_folder)+1:]
|
||||||
|
enabled_styles[plugin] = conf
|
||||||
|
|
||||||
http_conf = Config.get('backend.http')
|
http_conf = Config.get('backend.http')
|
||||||
return render_template('index.html', plugins=enabled_plugins,
|
return render_template('index.html', templates=enabled_templates,
|
||||||
hidden_plugins=hidden_plugins, utils=HttpUtils,
|
scripts=enabled_scripts, styles=enabled_styles,
|
||||||
token=Config.get('token'),
|
utils=HttpUtils, token=Config.get('token'),
|
||||||
websocket_port=get_websocket_port(),
|
websocket_port=get_websocket_port(),
|
||||||
has_ssl=http_conf.get('ssl_cert') is not None)
|
has_ssl=http_conf.get('ssl_cert') is not None)
|
||||||
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
|
|
||||||
ul.tab-nav {
|
|
||||||
list-style: none;
|
|
||||||
border-bottom: 1px solid #bbb;
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tab-nav li {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tab-nav li a.button {
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
margin-bottom: -1px;
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tab-nav li a.active.button {
|
|
||||||
border-bottom: 1px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content .tab-pane {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content .tab-pane.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
//// General purpose classes /////
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background: $selected-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pull-right {
|
||||||
|
text-align: right !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
//// UI elements definitions /////
|
||||||
|
|
||||||
|
@import 'common/elements/button';
|
||||||
|
@import 'common/elements/switch';
|
||||||
|
@import 'common/elements/slider';
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
button[disabled],
|
||||||
|
.button[disabled] {
|
||||||
|
color: #bbb;
|
||||||
|
background: rgba(240,240,240,1);
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #d8ffe0 !important;
|
||||||
|
border: 1px solid #98efb0 !important;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
.slider {
|
||||||
|
@include appearance(none);
|
||||||
|
@include transition(opacity .2s);
|
||||||
|
width: 100%;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: $slider-bg;
|
||||||
|
outline: none;
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: $slider-thumb-bg;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled]::-webkit-slider-thumb {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-moz-range-thumb {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
background: $slider-thumb-bg;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
@import url(https://fonts.googleapis.com/css?family=Francois+One);
|
||||||
|
@import url(https://fonts.googleapis.com/css?family=PT+Sans);
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Audiowide';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local("Audiowide"), local("Audiowide-Regular"), url(http://themes.googleusercontent.com/static/fonts/audiowide/v2/8XtYtNKEyyZh481XVWfVOj8E0i7KZn-EPnyo3HZu7kw.woff) format("woff");
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
user-select: none;
|
||||||
|
padding-top: 1rem;
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
label, label:before, label:after,
|
||||||
|
input[type=checkbox], input[type=checkbox]:before, input[type=checkbox]:after,
|
||||||
|
.feature, .feature:before, .feature:after {
|
||||||
|
transition: all 250ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
label:before, label:after,
|
||||||
|
input[type=checkbox]:before, input[type=checkbox]:after,
|
||||||
|
.feature:before, .feature:after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
position: relative;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
background-color: $switch-bg-1;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: $switch-shadow-1;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 1.4em;
|
||||||
|
transition: all 350ms ease-in;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 22.7272727273px;
|
||||||
|
height: 22.7272727273px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: $switch-bg-2;
|
||||||
|
box-shadow: $switch-shadow-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 35%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 4px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: $switch-bg-3;
|
||||||
|
box-shadow: $switch-shadow-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before, &:after {
|
||||||
|
transition-duration: 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
&:before { box-shadow: $switch-shadow-hover; }
|
||||||
|
&:after { background-color: $switch-bg-hover; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox]:checked + label {
|
||||||
|
box-shadow: $switch-shadow-checked-1;
|
||||||
|
&:before { box-shadow: $switch-shadow-checked-2; }
|
||||||
|
&:after { background-color: $switch-bg-checked; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&.glow {
|
||||||
|
label {
|
||||||
|
background-color: $switch-bg-glow-2;
|
||||||
|
box-shadow: $switch-shadow-glow-1;
|
||||||
|
|
||||||
|
&:before { box-shadow: $switch-shadow-glow-2; }
|
||||||
|
&:after {
|
||||||
|
background-color: $switch-bg-glow-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
label:hover {
|
||||||
|
&:before { box-shadow: $switch-shadow-glow-hover; }
|
||||||
|
&:after { background-color: $switch-bg-glow-hover; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox]:checked + label {
|
||||||
|
box-shadow: $switch-shadow-glow-checked-1;
|
||||||
|
&:before { box-shadow: $switch-shadow-glow-checked-2; }
|
||||||
|
&:after { background-color: $switch-bg-glow-checked; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
85
platypush/backend/http/static/css/source/common/layout.scss
Normal file
85
platypush/backend/http/static/css/source/common/layout.scss
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
$widths: (
|
||||||
|
s: '(max-width: 720px)',
|
||||||
|
m: '(max-width: 1024px) and (min-width: 720px)',
|
||||||
|
l: '(min-width: 1024px)',
|
||||||
|
);
|
||||||
|
|
||||||
|
@for $i from 1 through 12 {
|
||||||
|
.col-#{$i} {
|
||||||
|
float: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
@if $i < 12 {
|
||||||
|
width: (4.66666666667%*$i) + (4% * if($i > 1, $i - 1, 0));
|
||||||
|
margin-left: 4%;
|
||||||
|
} @else {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-no-margin-#{$i} {
|
||||||
|
float: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: ((100%/12)*$i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $i < 12 {
|
||||||
|
.col-offset-#{$i} {
|
||||||
|
margin-left: (8.66666666667%*$i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $size, $width in $widths {
|
||||||
|
@media #{$width} {
|
||||||
|
@for $i from 1 through 12 {
|
||||||
|
.col-#{$size}-#{$i} {
|
||||||
|
float: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
@if $i < 12 {
|
||||||
|
width: (4.66666666667%*$i) + (4% * if($i > 1, $i - 1, 0));
|
||||||
|
margin-left: 4%;
|
||||||
|
} @else {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $i < 12 {
|
||||||
|
.col-offset-#{$size}-#{$i} {
|
||||||
|
margin-left: (8.66666666667%*$i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-no-margin-#{$size}-#{$i} {
|
||||||
|
float: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: ((100%/12)*$i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$size}-hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$size}-visible {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
23
platypush/backend/http/static/css/source/common/mixins.scss
Normal file
23
platypush/backend/http/static/css/source/common/mixins.scss
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
@mixin appearance($value) {
|
||||||
|
-webkit-appearance: $value;
|
||||||
|
-ms-appearance: $value;
|
||||||
|
-o-appearance: $value;
|
||||||
|
-ms-appearance: $value;
|
||||||
|
appearance: $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin transition($value) {
|
||||||
|
-webkit-transition: $value;
|
||||||
|
-ms-transition: $value;
|
||||||
|
-o-transition: $value;
|
||||||
|
-ms-transition: $value;
|
||||||
|
transition: $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-shadow($value) {
|
||||||
|
-webkit-box-shadow: $value;
|
||||||
|
-o-box-shadow: $value;
|
||||||
|
-ms-box-shadow: $value;
|
||||||
|
box-shadow: $value;
|
||||||
|
}
|
||||||
|
|
36
platypush/backend/http/static/css/source/common/modal.scss
Normal file
36
platypush/backend/http/static/css/source/common/modal.scss
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 999;
|
||||||
|
background-color: rgba(10,10,10,0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
margin: 5% auto auto auto;
|
||||||
|
width: 70%;
|
||||||
|
background: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
margin: 0.5rem auto;
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
background-color: $modal-bg;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .1rem;
|
||||||
|
line-height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 2.5rem 2rem 1.5rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#notification-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 25em;
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
background: rgba(180,245,188,0.85);
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification:hover {
|
||||||
|
background: rgba(160,235,168,0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-title {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: capitalize;
|
||||||
|
line-height: 30px;
|
||||||
|
letter-spacing: .1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-body {
|
||||||
|
height: 6em;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
letter-spacing: .05rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-text {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-image {
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: .6em;
|
||||||
|
padding-bottom: .6em;
|
||||||
|
|
||||||
|
.notification-image-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa.notification-image-item {
|
||||||
|
margin-top: .8em;
|
||||||
|
margin-left: .2em;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
49
platypush/backend/http/static/css/source/common/vars.scss
Normal file
49
platypush/backend/http/static/css/source/common/vars.scss
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//// Common defaults
|
||||||
|
$default-bg: #f4f5f6 !default;
|
||||||
|
$default-fg: black !default;
|
||||||
|
$default-fg-2: #333333 !default;
|
||||||
|
|
||||||
|
$default-font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif !default;
|
||||||
|
$default-border: 1px solid #e1e4e8 !default;
|
||||||
|
$default-border-2: 1px solid #dddddd !default;
|
||||||
|
$default-bottom: $default-border !default;
|
||||||
|
$default-link-fg: #5f7869 !default;
|
||||||
|
|
||||||
|
$selected-bg: #c8ffd0 !default;
|
||||||
|
$hover-bg: #def6ea !default;
|
||||||
|
$header-bg: $default_bg !default;
|
||||||
|
$nav-bg: #e8e8e8 !default;
|
||||||
|
$nav-fg: $default-link-fg;
|
||||||
|
$modal-bg: #f0f0f0 !default;
|
||||||
|
|
||||||
|
//// Switch element
|
||||||
|
$switch-bg-1: #f9f8f6 !default;
|
||||||
|
$switch-bg-2: #38ffa0 !default;
|
||||||
|
$switch-bg-3: #cccccc !default;
|
||||||
|
$switch-bg-hover: #b3b3b3 !default;
|
||||||
|
$switch-bg-checked: #38ffa0 !default;
|
||||||
|
$switch-bg-glow-1: #111111 !default;
|
||||||
|
$switch-bg-glow-2: #ffffff !default;
|
||||||
|
$switch-bg-glow-3: #aaaaaa !default;
|
||||||
|
$switch-bg-glow-hover: #ffffff !default;
|
||||||
|
$switch-bg-glow-checked: #00e094 !default;
|
||||||
|
|
||||||
|
$switch-shadow-1: 0 5px 10px 0px #333, 0 15px 20px 0px #cccccc !default;
|
||||||
|
$switch-shadow-2: inset 0 0 0 5px #ccc, inset 0 0 0 14px #f9f8f6 !default;
|
||||||
|
$switch-shadow-3: 0 0 0 2.5px #f9f8f6 !default;
|
||||||
|
$switch-shadow-hover: inset 0 0 0 5px #b3b3b3, inset 0 0 0 14px #f9f8f6 !default;
|
||||||
|
$switch-shadow-checked-1: 0 2px 5px 0px gray, 0 15px 20px 0px transparent !default;
|
||||||
|
$switch-shadow-checked-2: inset 0 0 0 5px #38ffa0, inset 0 0 0 14px #f9f8f6 !default;
|
||||||
|
$switch-shadow-glow-1: 0 5px 10px 0 #aaa, 0 0 0 3px #bbb, 0 0 8px 2px transparent, 0 0 0 6px #eee !default;
|
||||||
|
$switch-shadow-glow-2: inset 0 0 0 5px #aaa, inset 0 0 0 14px #fff !default;
|
||||||
|
$switch-shadow-glow-hover: inset 0 0 0 5px #fff, inset 0 0 0 14px #fff !default;
|
||||||
|
$switch-shadow-glow-checked-1: 0 0px 8px 0 #00ad72, 0 0 0 3px #00e094, 0 0 30px 0 #00e094, 0 0 0 6px #fff !default;
|
||||||
|
$switch-shadow-glow-checked-2: inset 0 0 0 5px #00e094, inset 0 0 0 14px #fff !default;
|
||||||
|
|
||||||
|
//// Slier element
|
||||||
|
$slider-bg: #e4e4e4 !default;
|
||||||
|
$slider-thumb-bg: #4caf50 !default;
|
||||||
|
|
||||||
|
//// Header style
|
||||||
|
$header-bottom: $default-bottom;
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
header {
|
||||||
|
.row {
|
||||||
|
width: 100%;
|
||||||
|
background: $header-bg;
|
||||||
|
padding: 1rem 2.5rem;
|
||||||
|
// margin: 0 1rem 3.5rem -1rem;
|
||||||
|
border-bottom: $header-bottom;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 25px;
|
||||||
|
|
||||||
|
.logo-1 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 3rem;
|
||||||
|
|
||||||
|
.date {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
32
platypush/backend/http/static/css/source/webpanel/index.scss
Normal file
32
platypush/backend/http/static/css/source/webpanel/index.scss
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
@import 'common/vars';
|
||||||
|
|
||||||
|
@import 'common/mixins';
|
||||||
|
@import 'common/layout';
|
||||||
|
@import 'common/elements';
|
||||||
|
@import 'common/modal';
|
||||||
|
@import 'common/notification';
|
||||||
|
|
||||||
|
@import 'header';
|
||||||
|
@import 'nav';
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
font-family: $default-font-family;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $default-link-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-container {
|
||||||
|
border: $default-border-2;
|
||||||
|
border-radius: 1rem;
|
||||||
|
margin: 1.5rem;
|
||||||
|
box-shadow: 8px 8px 6px -1px rgba(187,187,187,0.75);
|
||||||
|
}
|
||||||
|
|
61
platypush/backend/http/static/css/source/webpanel/nav.scss
Normal file
61
platypush/backend/http/static/css/source/webpanel/nav.scss
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
nav {
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
background: $nav-bg;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: $default-bottom;
|
||||||
|
box-shadow: 0 2.5px 4px 0 #bbb;
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .1rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-radius: 2rem;
|
||||||
|
background: $hover-bg;
|
||||||
|
letter-spacing: .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
color: $nav-fg;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $nav-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.decorator {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 20px solid transparent;
|
||||||
|
border-bottom: 20px solid transparent;
|
||||||
|
border-left: 17px solid $selected-bg;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.decorator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.selected {
|
||||||
|
border-radius: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
@import 'common/vars';
|
||||||
|
@import 'common/layout';
|
||||||
|
@import 'webpanel/plugins/light.hue/vars';
|
||||||
|
|
||||||
|
.light-hue-container {
|
||||||
|
display: flex;
|
||||||
|
color: $default-fg-2;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 3.8rem;
|
||||||
|
letter-spacing: .1rem;
|
||||||
|
|
||||||
|
.groups,
|
||||||
|
.scenes,
|
||||||
|
.units {
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-right: $default-border-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: .75rem;
|
||||||
|
background: $default-bg;
|
||||||
|
border-bottom: $default-border-2;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 1rem 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group,
|
||||||
|
.scene,
|
||||||
|
.unit,
|
||||||
|
.group-controller {
|
||||||
|
padding: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $hover-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: $default-border-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.hidden) {
|
||||||
|
.row {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* > .properties {
|
||||||
|
margin: 1.5rem auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
font-weight: 100;
|
||||||
|
border: $default-border-2;
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
background: $light-hue-properties-bg;
|
||||||
|
box-shadow: $light-hue-properties-shadow;
|
||||||
|
|
||||||
|
.slider-container {
|
||||||
|
@extend .vertical-center;
|
||||||
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
* > .fa {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
* > .color-logo {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
* > .color-logo-red { background-color: red; }
|
||||||
|
* > .color-logo-green { background-color: green; }
|
||||||
|
* > .color-logo-blue { background-color: blue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
* > .properties {
|
||||||
|
background: $light-hue-properties-hover-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-controller {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.groups {
|
||||||
|
.title {
|
||||||
|
border-radius: 1rem 0 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.units {
|
||||||
|
.title {
|
||||||
|
border-radius: 0 1rem 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
$light-hue-properties-bg: rgba(239,239,240,0.5);
|
||||||
|
$light-hue-properties-hover-bg: white;
|
||||||
|
$light-hue-properties-shadow: 0 0 4px 2px rgba(187,187,187,0.75);
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
@import url(https://fonts.googleapis.com/css?family=Francois+One);
|
|
||||||
@import url(https://fonts.googleapis.com/css?family=PT+Sans);
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Audiowide';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
src: local("Audiowide"), local("Audiowide-Regular"), url(http://themes.googleusercontent.com/static/fonts/audiowide/v2/8XtYtNKEyyZh481XVWfVOj8E0i7KZn-EPnyo3HZu7kw.woff) format("woff"); }
|
|
||||||
|
|
||||||
.toggle {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
user-select: none; }
|
|
||||||
|
|
||||||
.toggle--checkbox {
|
|
||||||
display: none !important; }
|
|
||||||
|
|
||||||
.toggle--btn {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
font-size: 1.4em;
|
|
||||||
transition: all 350ms ease-in; }
|
|
||||||
.toggle--btn:hover {
|
|
||||||
cursor: pointer; }
|
|
||||||
|
|
||||||
.toggle--btn, .toggle--btn:before, .toggle--btn:after,
|
|
||||||
.toggle--checkbox,
|
|
||||||
.toggle--checkbox:before,
|
|
||||||
.toggle--checkbox:after,
|
|
||||||
.toggle--feature,
|
|
||||||
.toggle--feature:before,
|
|
||||||
.toggle--feature:after {
|
|
||||||
transition: all 250ms ease-in; }
|
|
||||||
.toggle--btn:before, .toggle--btn:after,
|
|
||||||
.toggle--checkbox:before,
|
|
||||||
.toggle--checkbox:after,
|
|
||||||
.toggle--feature:before,
|
|
||||||
.toggle--feature:after {
|
|
||||||
content: '';
|
|
||||||
display: block; }
|
|
||||||
|
|
||||||
/* =====================================================
|
|
||||||
Toggle - switch stylee
|
|
||||||
===================================================== */
|
|
||||||
.toggle--switch .toggle--btn {
|
|
||||||
position: relative;
|
|
||||||
width: 120px;
|
|
||||||
height: 44px;
|
|
||||||
font-family: 'PT Sans', Sans Serif;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: #fff;
|
|
||||||
background: linear-gradient(90deg, #a4bf4d 0%, #a4bf4d 50%, #ca5046 50%, #ca5046 200%);
|
|
||||||
background-position: -80px 0;
|
|
||||||
background-size: 200% 100%;
|
|
||||||
box-shadow: inset 0 0px 22px -8px #111; }
|
|
||||||
.toggle--switch .toggle--btn, .toggle--switch .toggle--btn:before {
|
|
||||||
border-radius: 4px; }
|
|
||||||
.toggle--switch .toggle--btn:before {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
width: 52px;
|
|
||||||
height: 44px;
|
|
||||||
border: 2px solid #202027;
|
|
||||||
background-image: linear-gradient(90deg, transparent 50%, rgba(255, 255, 255, 0.15) 100%);
|
|
||||||
background-color: #2b2e3a;
|
|
||||||
background-size: 5px 5px;
|
|
||||||
text-indent: -100%; }
|
|
||||||
.toggle--switch .toggle--feature {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 44px;
|
|
||||||
text-shadow: 0 1px 2px #666; }
|
|
||||||
.toggle--switch .toggle--feature:before, .toggle--switch .toggle--feature:after {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%); }
|
|
||||||
.toggle--switch .toggle--feature:before {
|
|
||||||
content: attr(data-label-on);
|
|
||||||
left: -60%; }
|
|
||||||
.toggle--switch .toggle--feature:after {
|
|
||||||
content: attr(data-label-off);
|
|
||||||
right: 16%; }
|
|
||||||
.toggle--switch .toggle--checkbox:checked + .toggle--btn {
|
|
||||||
background-position: 0 0; }
|
|
||||||
.toggle--switch .toggle--checkbox:checked + .toggle--btn:before {
|
|
||||||
left: calc(100% - 52px); }
|
|
||||||
.toggle--switch .toggle--checkbox:checked + .toggle--btn .toggle--feature:before {
|
|
||||||
left: 20%; }
|
|
||||||
.toggle--switch .toggle--checkbox:checked + .toggle--btn .toggle--feature:after {
|
|
||||||
right: -60%; }
|
|
||||||
|
|
||||||
/* ======================================================
|
|
||||||
Push button toggle
|
|
||||||
====================================================== */
|
|
||||||
.toggle--push .toggle--btn {
|
|
||||||
position: relative;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
background-color: #f9f8f6;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 5px 10px 0px #333, 0 15px 20px 0px #cccccc; }
|
|
||||||
.toggle--push .toggle--btn, .toggle--push .toggle--btn:before, .toggle--push .toggle--btn:after {
|
|
||||||
transition-duration: 150ms; }
|
|
||||||
.toggle--push .toggle--btn:before {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 22.7272727273px;
|
|
||||||
height: 22.7272727273px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #38ffa0;
|
|
||||||
box-shadow: inset 0 0 0 5px #ccc, inset 0 0 0 14px #f9f8f6; }
|
|
||||||
.toggle--push .toggle--btn:after {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 35%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 4px;
|
|
||||||
height: 12px;
|
|
||||||
background-color: #ccc;
|
|
||||||
box-shadow: 0 0 0 2.5px #f9f8f6; }
|
|
||||||
.toggle--push .toggle--btn:hover:before {
|
|
||||||
box-shadow: inset 0 0 0 5px #b3b3b3, inset 0 0 0 14px #f9f8f6; }
|
|
||||||
.toggle--push .toggle--btn:hover:after {
|
|
||||||
background-color: #b3b3b3; }
|
|
||||||
.toggle--push .toggle--checkbox:checked + .toggle--btn {
|
|
||||||
box-shadow: 0 2px 5px 0px gray, 0 15px 20px 0px transparent; }
|
|
||||||
.toggle--push .toggle--checkbox:checked + .toggle--btn:before {
|
|
||||||
box-shadow: inset 0 0 0 5px #38ffa0, inset 0 0 0 14px #f9f8f6; }
|
|
||||||
.toggle--push .toggle--checkbox:checked + .toggle--btn:after {
|
|
||||||
background-color: #38ffa0; }
|
|
||||||
|
|
||||||
.toggle--push--glow {
|
|
||||||
background: #111;
|
|
||||||
padding: 50px 0;
|
|
||||||
margin-bottom: -50px; }
|
|
||||||
.toggle--push--glow .toggle--btn {
|
|
||||||
background-color: #dfdfdf;
|
|
||||||
box-shadow: 0 5px 10px 0px #333, 0 0 0 3px #444444, 0 0 8px 2px transparent, 0 0 0 6px #919191; }
|
|
||||||
.toggle--push--glow .toggle--btn:before {
|
|
||||||
box-shadow: inset 0 0 0 5px #aaa, inset 0 0 0 14px #dfdfdf; }
|
|
||||||
.toggle--push--glow .toggle--btn:after {
|
|
||||||
background-color: #aaa;
|
|
||||||
box-shadow: 0 0 0 2.5px #dfdfdf; }
|
|
||||||
.toggle--push--glow .toggle--btn:hover:before {
|
|
||||||
box-shadow: inset 0 0 0 5px #777777, inset 0 0 0 14px #dfdfdf; }
|
|
||||||
.toggle--push--glow .toggle--btn:hover:after {
|
|
||||||
background-color: #777777; }
|
|
||||||
.toggle--push--glow .toggle--checkbox:checked + .toggle--btn {
|
|
||||||
box-shadow: 0 0px 8px 0 #0072ad, 0 0 0 3px #0094e0, 0 0 30px 0 #0094e0, 0 0 0 6px #777777; }
|
|
||||||
.toggle--push--glow .toggle--checkbox:checked + .toggle--btn:before {
|
|
||||||
box-shadow: inset 0 0 0 5px #0094e0, inset 0 0 0 14px #dfdfdf; }
|
|
||||||
.toggle--push--glow .toggle--checkbox:checked + .toggle--btn:after {
|
|
||||||
background-color: #0094e0; }
|
|
||||||
|
|
43
platypush/backend/http/static/js/api.js
Normal file
43
platypush/backend/http/static/js/api.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
function execute(request) {
|
||||||
|
var additionalPayload = {};
|
||||||
|
|
||||||
|
if (!('target' in request) || !request['target']) {
|
||||||
|
request['target'] = 'localhost';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('type' in request) || !request['type']) {
|
||||||
|
request['type'] = 'request';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.config.token) {
|
||||||
|
additionalPayload.headers = {
|
||||||
|
'X-Token': window.config.token
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.post('/execute', request, additionalPayload)
|
||||||
|
.then((response) => {
|
||||||
|
response = response.data.response;
|
||||||
|
if (!response.errors.length) {
|
||||||
|
resolve(response.output);
|
||||||
|
} else {
|
||||||
|
// TODO Handle error
|
||||||
|
reject(response.errors[0]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// TODO Handle error
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function request(action, args={}) {
|
||||||
|
return execute({
|
||||||
|
type: 'request',
|
||||||
|
action: action,
|
||||||
|
args: args,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,32 +1,46 @@
|
||||||
// Declaration of the main vue app
|
Vue.component('app-header', {
|
||||||
var app;
|
template: '#tmpl-app-header',
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
now: new Date(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
function ready(callback){
|
created: function() {
|
||||||
if (document.readyState!='loading') callback();
|
const self = this;
|
||||||
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);
|
setInterval(() => {
|
||||||
else document.attachEvent('onreadystatechange', function(){
|
self.now = new Date();
|
||||||
if (document.readyState=='complete') callback();
|
}, 1000)
|
||||||
});
|
},
|
||||||
}
|
|
||||||
|
|
||||||
ready(function() {
|
|
||||||
app = new Vue({
|
|
||||||
el: '#app',
|
|
||||||
delimiters: ['[[',']]'],
|
|
||||||
data: {
|
|
||||||
config: {foo:"bar"}
|
|
||||||
},
|
|
||||||
|
|
||||||
created: function() {
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted: function() {
|
|
||||||
},
|
|
||||||
|
|
||||||
updated: function() {
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyed: function() {
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Vue.component('plugin', {
|
||||||
|
template: '#tmpl-plugin',
|
||||||
|
props: ['config','tag'],
|
||||||
|
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
selected: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Declaration of the main vue app
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
// Override {{ }} delimiters to prevent clash with Flask templates
|
||||||
|
delimiters: ['[[',']]'],
|
||||||
|
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
config: window.config,
|
||||||
|
selectedPlugin: undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted: function() {},
|
||||||
|
created: function() {},
|
||||||
|
updated: function() {},
|
||||||
|
destroyed: function() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
14
platypush/backend/http/static/js/elements.js
Normal file
14
platypush/backend/http/static/js/elements.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Vue.component('toggle-switch', {
|
||||||
|
template: '#tmpl-switch',
|
||||||
|
props: ['id','value','glow'],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggled: function(event) {
|
||||||
|
this.$emit('toggled', {
|
||||||
|
id: this.id,
|
||||||
|
value: !this.value
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
9
platypush/backend/http/static/js/lib/axios.min.js
vendored
Normal file
9
platypush/backend/http/static/js/lib/axios.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
106
platypush/backend/http/static/js/plugins/light.hue/converter.js
Normal file
106
platypush/backend/http/static/js/plugins/light.hue/converter.js
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// Source: https://gist.github.com/uredkar/bd305f2dda9abf5b393d417424777c87#file-cie_rgb_converter-js
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts CIE color space to RGB color space
|
||||||
|
* @param {Number} x
|
||||||
|
* @param {Number} y
|
||||||
|
* @param {Number} brightness - Ranges from 1 to 254
|
||||||
|
* @return {Array} Array that contains the color values for red, green and blue
|
||||||
|
*/
|
||||||
|
function toRGB(x, y, brightness) {
|
||||||
|
//Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons)
|
||||||
|
if (brightness === undefined) {
|
||||||
|
brightness = 254;
|
||||||
|
}
|
||||||
|
|
||||||
|
var z = 1.0 - x - y;
|
||||||
|
var Y = (brightness / 254).toFixed(2);
|
||||||
|
var X = (Y / y) * x;
|
||||||
|
var Z = (Y / y) * z;
|
||||||
|
|
||||||
|
//Convert to RGB using Wide RGB D65 conversion
|
||||||
|
var red = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
|
||||||
|
var green = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
|
||||||
|
var blue = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
|
||||||
|
|
||||||
|
//If red, green or blue is larger than 1.0 set it back to the maximum of 1.0
|
||||||
|
if (red > blue && red > green && red > 1.0) {
|
||||||
|
|
||||||
|
green = green / red;
|
||||||
|
blue = blue / red;
|
||||||
|
red = 1.0;
|
||||||
|
}
|
||||||
|
else if (green > blue && green > red && green > 1.0) {
|
||||||
|
|
||||||
|
red = red / green;
|
||||||
|
blue = blue / green;
|
||||||
|
green = 1.0;
|
||||||
|
}
|
||||||
|
else if (blue > red && blue > green && blue > 1.0) {
|
||||||
|
|
||||||
|
red = red / blue;
|
||||||
|
green = green / blue;
|
||||||
|
blue = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reverse gamma correction
|
||||||
|
red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, (1.0 / 2.4)) - 0.055;
|
||||||
|
green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, (1.0 / 2.4)) - 0.055;
|
||||||
|
blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, (1.0 / 2.4)) - 0.055;
|
||||||
|
|
||||||
|
|
||||||
|
//Convert normalized decimal to decimal
|
||||||
|
red = Math.round(red * 255);
|
||||||
|
green = Math.round(green * 255);
|
||||||
|
blue = Math.round(blue * 255);
|
||||||
|
|
||||||
|
if (isNaN(red))
|
||||||
|
red = 0;
|
||||||
|
|
||||||
|
if (isNaN(green))
|
||||||
|
green = 0;
|
||||||
|
|
||||||
|
if (isNaN(blue))
|
||||||
|
blue = 0;
|
||||||
|
|
||||||
|
|
||||||
|
return [red, green, blue];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts RGB color space to CIE color space
|
||||||
|
* @param {Number} red
|
||||||
|
* @param {Number} green
|
||||||
|
* @param {Number} blue
|
||||||
|
* @return {Array} Array that contains the CIE color values for x and y
|
||||||
|
*/
|
||||||
|
function toXY(red, green, blue) {
|
||||||
|
if (red > 1) { red /= 255; }
|
||||||
|
if (green > 1) { green /= 255; }
|
||||||
|
if (blue > 1) { blue /= 255; }
|
||||||
|
|
||||||
|
//Apply a gamma correction to the RGB values, which makes the color more vivid and more the like the color displayed on the screen of your device
|
||||||
|
var red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
|
||||||
|
var green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92);
|
||||||
|
var blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92);
|
||||||
|
|
||||||
|
//RGB values to XYZ using the Wide RGB D65 conversion formula
|
||||||
|
var X = red * 0.664511 + green * 0.154324 + blue * 0.162028;
|
||||||
|
var Y = red * 0.283881 + green * 0.668433 + blue * 0.047685;
|
||||||
|
var Z = red * 0.000088 + green * 0.072310 + blue * 0.986039;
|
||||||
|
|
||||||
|
//Calculate the xy values from the XYZ values
|
||||||
|
var x = (X / (X + Y + Z)).toFixed(4);
|
||||||
|
var y = (Y / (X + Y + Z)).toFixed(4);
|
||||||
|
|
||||||
|
if (isNaN(x))
|
||||||
|
x = 0;
|
||||||
|
|
||||||
|
if (isNaN(y))
|
||||||
|
y = 0;
|
||||||
|
|
||||||
|
|
||||||
|
return [x, y];
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
Vue.component('light-hue-property-selector', {
|
||||||
|
template: '#tmpl-light-hue-property-selector',
|
||||||
|
props: ['id','value'],
|
||||||
|
computed: {
|
||||||
|
rgb: function() {
|
||||||
|
if (!(this.value && 'xy' in this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toRGB(this.value.xy[0], this.value.xy[1], this.value.bri);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
changed: function(event) {
|
||||||
|
var value = parseInt(event.target.value);
|
||||||
|
var xy;
|
||||||
|
|
||||||
|
if (event.target.getAttribute('class').split(' ').indexOf('bri') > -1) {
|
||||||
|
this.$emit('bri-changed', {bri: value});
|
||||||
|
return;
|
||||||
|
} else if (event.target.getAttribute('class').split(' ').indexOf('ct') > -1) {
|
||||||
|
this.$emit('ct-changed', {ct: value});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.getAttribute('class').split(' ').indexOf('red') > -1) {
|
||||||
|
xy = toXY(value, this.rgb[1], this.rgb[2]);
|
||||||
|
} else if (event.target.getAttribute('class').split(' ').indexOf('green') > -1) {
|
||||||
|
xy = toXY(this.rgb[0], value, this.rgb[2]);
|
||||||
|
} else if (event.target.getAttribute('class').split(' ').indexOf('blue') > -1) {
|
||||||
|
xy = toXY(this.rgb[0], this.rgb[1], value);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('color-changed', {xy: xy});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
118
platypush/backend/http/static/js/plugins/light.hue/groups.js
Normal file
118
platypush/backend/http/static/js/plugins/light.hue/groups.js
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
Vue.component('light-hue-group', {
|
||||||
|
template: '#tmpl-light-hue-group',
|
||||||
|
props: ['id','name'],
|
||||||
|
});
|
||||||
|
|
||||||
|
Vue.component('light-hue-group-controller', {
|
||||||
|
template: '#tmpl-light-hue-group-controller',
|
||||||
|
props: ['id','groups','value','collapsed'],
|
||||||
|
computed: {
|
||||||
|
lights: function() {
|
||||||
|
return this.groups[this.id].lights;
|
||||||
|
},
|
||||||
|
|
||||||
|
name: function() {
|
||||||
|
return this.groups[this.id].name;
|
||||||
|
},
|
||||||
|
|
||||||
|
properties: function() {
|
||||||
|
var self = this;
|
||||||
|
var avg = function(values) {
|
||||||
|
if (values.length) {
|
||||||
|
return values.reduce((sum,value) => sum+value) / values.length;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var getLightValues = function(attribute) {
|
||||||
|
return Object.values(self.lights).map(
|
||||||
|
light => attribute in light.state && light.state.on ? light.state[attribute] : undefined
|
||||||
|
).filter(value => value !== undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
xy: [
|
||||||
|
avg(getLightValues('xy').map(_ => parseFloat(_[0]))),
|
||||||
|
avg(getLightValues('xy').map(_ => parseFloat(_[1])))
|
||||||
|
],
|
||||||
|
ct: avg(getLightValues('ct')),
|
||||||
|
bri: avg(getLightValues('bri')),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggled: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.' + (event.value ? 'on' : 'off'),
|
||||||
|
{ groups: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
lights: this._updateLights('on', event.value),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$emit('input', {...this.value, state: {...this.value.state, any_on: event.value, all_on: event.value}});
|
||||||
|
},
|
||||||
|
|
||||||
|
propertiesCollapsedToggled: function() {
|
||||||
|
this.$emit('properties-collapsed-toggled', {
|
||||||
|
type: 'group',
|
||||||
|
id: this.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
colorChanged: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.xy',
|
||||||
|
{ value: event.xy, groups: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
lights: this._updateLights('xy', event.xy),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
briChanged: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.bri',
|
||||||
|
{ value: event.bri, groups: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
lights: this._updateLights('bri', event.bri),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
ctChanged: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.ct',
|
||||||
|
{ value: event.ct, groups: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
lights: this._updateLights('ct', event.ct),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLights: function(attr, value) {
|
||||||
|
var lights = [];
|
||||||
|
for (const light of Object.values(this.value.lights)) {
|
||||||
|
var state = light.state;
|
||||||
|
state[attr] = value;
|
||||||
|
lights.push({
|
||||||
|
...light,
|
||||||
|
state: state,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return lights;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
162
platypush/backend/http/static/js/plugins/light.hue/index.js
Normal file
162
platypush/backend/http/static/js/plugins/light.hue/index.js
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
Vue.component('light-hue', {
|
||||||
|
template: '#tmpl-light-hue',
|
||||||
|
props: ['config'],
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
groups: {},
|
||||||
|
lights: {},
|
||||||
|
scenes: {},
|
||||||
|
selectedGroup: undefined,
|
||||||
|
selectedScene: undefined,
|
||||||
|
selectedProperties: {
|
||||||
|
type: undefined,
|
||||||
|
id: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
_prepareGroups: function() {
|
||||||
|
for (const [groupId, group] of Object.entries(this.groups)) {
|
||||||
|
if (group.type !== 'Room' || group.recycle) {
|
||||||
|
delete this.groups[groupId];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.groups[groupId].scenes = {};
|
||||||
|
var lights = {};
|
||||||
|
|
||||||
|
for (const lightId of this.groups[groupId].lights) {
|
||||||
|
lights[lightId] = this.lights[lightId];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.groups[groupId].lights = lights;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_prepareScenes: function() {
|
||||||
|
for (const [sceneId, scene] of Object.entries(this.scenes)) {
|
||||||
|
if (scene.recycle) {
|
||||||
|
delete this.scenes[sceneId];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scenes[sceneId].groups = {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_linkLights: function() {
|
||||||
|
// Special group for lights with no group
|
||||||
|
this.groups[-1] = {
|
||||||
|
type: undefined,
|
||||||
|
lights: {},
|
||||||
|
scenes: {},
|
||||||
|
name: "[No Group]",
|
||||||
|
recycle: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [lightId, light] of Object.entries(this.lights)) {
|
||||||
|
this.lights[lightId].groups = {};
|
||||||
|
|
||||||
|
for (const [groupId, group] of Object.entries(this.groups)) {
|
||||||
|
if (lightId in group.lights) {
|
||||||
|
this.lights[lightId].groups[groupId] = group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!light.groups.length) {
|
||||||
|
this.groups[-1].lights[lightId] = light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.groups[-1].lights.length) {
|
||||||
|
delete this.groups[-1];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_linkScenes: function() {
|
||||||
|
for (const [sceneId, scene] of Object.entries(this.scenes)) {
|
||||||
|
for (const lightId of scene.lights) {
|
||||||
|
for (const [groupId, group] of Object.entries(this.lights[lightId].groups)) {
|
||||||
|
this.scenes[sceneId].groups[groupId] = group;
|
||||||
|
this.groups[groupId].scenes[sceneId] = scene;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh: async function() {
|
||||||
|
const getLights = request('light.hue.get_lights');
|
||||||
|
const getGroups = request('light.hue.get_groups');
|
||||||
|
const getScenes = request('light.hue.get_scenes');
|
||||||
|
|
||||||
|
[this.lights, this.groups, this.scenes] = await Promise.all([getLights, getGroups, getScenes]);
|
||||||
|
|
||||||
|
this._prepareGroups();
|
||||||
|
this._prepareScenes();
|
||||||
|
this._linkLights();
|
||||||
|
this._linkScenes();
|
||||||
|
},
|
||||||
|
|
||||||
|
updatedGroup: function(event) {
|
||||||
|
for (const light of Object.values(this.groups[this.selectedGroup].lights)) {
|
||||||
|
if (event.state.any_on === event.state.all_on) {
|
||||||
|
light.state.on = event.state.all_on;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attr in ['bri', 'xy', 'ct']) {
|
||||||
|
if (attr in event.state) {
|
||||||
|
light.state[attr] = event.state[attr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectScene: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.scene', {
|
||||||
|
name: event.name,
|
||||||
|
groups: [this.groups[this.selectedGroup].name],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.selectedScene = event.id;
|
||||||
|
for (const light of Object.values(this.scenes[this.selectedScene].lights)) {
|
||||||
|
this.lights[light].state.on = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
collapsedToggled: function(event) {
|
||||||
|
if (event.type == this.selectedProperties.type
|
||||||
|
&& event.id == this.selectedProperties.id) {
|
||||||
|
this.selectedProperties = {
|
||||||
|
type: undefined,
|
||||||
|
id: undefined,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.selectedProperties = {
|
||||||
|
type: event.type,
|
||||||
|
id: event.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnitInput: function(event) {
|
||||||
|
var groups = this.lights[event.id].groups;
|
||||||
|
for (const [groupId, group] of Object.entries(groups)) {
|
||||||
|
if (event.on) {
|
||||||
|
this.groups[groupId].state.any_on = true;
|
||||||
|
this.groups[groupId].state.all_on = Object.values(group.lights).filter((l) => l.state.on).length === Object.values(group.lights).length;
|
||||||
|
} else {
|
||||||
|
this.groups[groupId].state.all_on = false;
|
||||||
|
this.groups[groupId].state.any_on = Object.values(group.lights).filter((l) => l.state.on).length > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created: function() {
|
||||||
|
this.refresh();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
11
platypush/backend/http/static/js/plugins/light.hue/scenes.js
Normal file
11
platypush/backend/http/static/js/plugins/light.hue/scenes.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Vue.component('light-hue-scene', {
|
||||||
|
template: '#tmpl-light-hue-scene',
|
||||||
|
props: ['id','name'],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
clicked: function(event) {
|
||||||
|
this.$emit('input', {id: this.id, name: this.name});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
69
platypush/backend/http/static/js/plugins/light.hue/units.js
Normal file
69
platypush/backend/http/static/js/plugins/light.hue/units.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
Vue.component('light-hue-unit', {
|
||||||
|
template: '#tmpl-light-hue-unit',
|
||||||
|
props: ['id','capabilities','config','name',
|
||||||
|
'uniqueid','type','productname','modelid',
|
||||||
|
'manufacturername', 'swupdate','swversion','value',
|
||||||
|
'collapsed'],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggled: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.' + (event.value ? 'on' : 'off'),
|
||||||
|
{ lights: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
id: this.id,
|
||||||
|
on: event.value
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
colorChanged: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.xy',
|
||||||
|
{ value: event.xy, lights: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
id: this.id,
|
||||||
|
xy: event.xy
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
briChanged: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.bri',
|
||||||
|
{ value: event.bri, lights: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
id: this.id,
|
||||||
|
bri: event.bri
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
ctChanged: async function(event) {
|
||||||
|
await request(
|
||||||
|
'light.hue.ct',
|
||||||
|
{ value: event.ct, lights: [this.id] },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
...this.value,
|
||||||
|
id: this.id,
|
||||||
|
ct: event.ct
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
propertiesCollapsedToggled: function() {
|
||||||
|
this.$emit('properties-collapsed-toggled', {
|
||||||
|
type: 'unit',
|
||||||
|
id: this.id
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
|
|
||||||
$(function() {
|
|
||||||
$('ul.tab-nav li a.button').click(function() {
|
|
||||||
var href = $(this).attr('href');
|
|
||||||
|
|
||||||
$('li a.active.button', $(this).parent().parent()).removeClass('active');
|
|
||||||
$(this).addClass('active');
|
|
||||||
|
|
||||||
$('.tab-pane.active', $(href).parent()).removeClass('active');
|
|
||||||
$(href).addClass('active');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
2
platypush/backend/http/templates/elements.html
Normal file
2
platypush/backend/http/templates/elements.html
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{% include 'elements/switch.html' %}
|
||||||
|
|
7
platypush/backend/http/templates/elements/switch.html
Normal file
7
platypush/backend/http/templates/elements/switch.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<script type="text/x-template" id="tmpl-switch">
|
||||||
|
<div class="switch" :class="{glow: glow}" @click="toggled">
|
||||||
|
<input type="checkbox" v-model="value">
|
||||||
|
<label></label>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
18
platypush/backend/http/templates/header.html
Normal file
18
platypush/backend/http/templates/header.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script type="text/x-template" id="tmpl-app-header">
|
||||||
|
<header class="s-hidden m-hidden">
|
||||||
|
<div class="row">
|
||||||
|
<div class="logo col-9">
|
||||||
|
<span class="logo-1">Platypush</span>
|
||||||
|
<span class="logo-2">Web Panel</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="date-time col-3">
|
||||||
|
<div class="date" v-text="now.toDateString().substring(0,10)"></div>
|
||||||
|
<div class="time" v-text="now.toTimeString().substring(0,8)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<app-header></app-header>
|
||||||
|
|
|
@ -1,75 +1,73 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<head>
|
<head>
|
||||||
<title>Platypush Web Console</title>
|
<title>Platypush Web Panel</title>
|
||||||
|
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton.css') }}">
|
||||||
<!-- <link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton-tabs.css') }}"> -->
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='font-awesome/css/font-awesome.min.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='font-awesome/css/font-awesome.min.css') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/application.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/dist/webpanel.css') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/toggles.css') }}">
|
|
||||||
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/vue.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/vue.js') }}"></script>
|
||||||
<!--<script type="text/javascript" src="{{ url_for('static', filename='js/vue.min.js') }}"></script>-->
|
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/axios.min.js') }}"></script>
|
||||||
|
|
||||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.3.1.min.js') }}"></script> -->
|
<script type="text/javascript" src="{{ url_for('static', filename='js/api.js') }}"></script>
|
||||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/jquery-ui-1.12.1.min.js') }}"></script> -->
|
|
||||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/skeleton-tabs.js') }}"></script> -->
|
{% for style in styles.values() %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename=style['_style_file']) }}">
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
|
||||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/pushbullet.js') }}"></script> -->
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
if (!window.config) {
|
if (!window.config) {
|
||||||
window.config = {};
|
window.config = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
window.config = { ...window.config,
|
window.config = { ...window.config,
|
||||||
websocket_port: {% print(websocket_port) %},
|
websocket_port: {{ websocket_port }},
|
||||||
has_ssl: {% print('true' if has_ssl else 'false') %},
|
has_ssl: {% print('true' if has_ssl else 'false') %},
|
||||||
plugins: JSON.parse('{% print(utils.to_json(plugins))|safe %}'),
|
templates: JSON.parse('{% print(utils.to_json(templates))|safe %}'),
|
||||||
|
scripts: JSON.parse('{% print(utils.to_json(scripts))|safe %}'),
|
||||||
};
|
};
|
||||||
|
|
||||||
{% if token %}
|
{% if token %}
|
||||||
window.config.token = '{% print(token) %}';
|
window.config.token = '{{ token }}';
|
||||||
{% else %}
|
{% else %}
|
||||||
window.config.token = undefined;
|
window.config.token = undefined;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{% include 'elements.html' %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<div id="app">
|
||||||
<div class="row">
|
{% include 'header.html' %}
|
||||||
<div class="logo nine columns">
|
|
||||||
<span class="logo-1">Platypush</span>
|
{% with plugins=templates.keys() %}
|
||||||
<span class="logo-2">Web Panel</span>
|
{% include 'nav.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="plugins-container">
|
||||||
|
{% for plugin, conf in templates.items() %}
|
||||||
|
{% with configuration=templates[plugin], utils=utils %}
|
||||||
|
{% include conf['_template_file'] %}
|
||||||
|
<plugin tag="{{ utils.plugin_name_to_tag(plugin) }}"
|
||||||
|
:config="{{ conf }}" :class="{hidden: '{{ plugin }}' != selectedPlugin}"/>
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="date-time" class="three columns">
|
{% include 'plugins/template.html' %}
|
||||||
<div class="date"></div>
|
|
||||||
<div class="time"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<nav>
|
{% for script in scripts.values() %}
|
||||||
{% for plugin in plugins.keys()|sort() %}
|
<script type="text/javascript" src="{{ url_for('static', filename=script['_script_file']) }}"></script>
|
||||||
<a href="#{% print plugin %}">
|
{% endfor %}
|
||||||
{% print plugin %}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main>
|
<script type="text/javascript" src="{{ url_for('static', filename='js/elements.js') }}"></script>
|
||||||
<div id="app">
|
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
||||||
{% for plugin in plugins.keys()|sort() %}
|
|
||||||
{% with configuration=plugins[plugin], utils=utils %}
|
|
||||||
<div class="tab-pane plugin-tab-content" id="{% print plugin %}-container">
|
|
||||||
{% include 'panel/' + plugin + '/index.html' %}
|
|
||||||
</div>
|
|
||||||
{% endwith %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<head>
|
|
||||||
<title>Platypush Web Console</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton-tabs.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='font-awesome/css/font-awesome.min.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/application.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/toggles.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/jquery-ui.css') }}">
|
|
||||||
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.3.1.min.js') }}"></script>
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-ui-1.12.1.min.js') }}"></script>
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/skeleton-tabs.js') }}"></script>
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='js/pushbullet.js') }}"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.websocket_port = {% print(websocket_port) %};
|
|
||||||
window.has_ssl = {% print('true' if has_ssl else 'false') %};
|
|
||||||
|
|
||||||
{% if token %}
|
|
||||||
window.token = '{% print(token) %}'
|
|
||||||
{% else %}
|
|
||||||
window.token = undefined
|
|
||||||
{% endif %}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<div class="row">
|
|
||||||
<div class="logo nine columns">
|
|
||||||
<span class="logo-1">Platypush</span>
|
|
||||||
<span class="logo-2">Web Panel</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="date-time" class="three columns">
|
|
||||||
<div class="date"></div>
|
|
||||||
<div class="time"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<ul class="tab-nav">
|
|
||||||
{% for plugin in plugins.keys()|sort() %}
|
|
||||||
<li>
|
|
||||||
<a class="button plugin-tab-item" href="#{% print plugin %}">
|
|
||||||
{% print plugin %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="tab-content">
|
|
||||||
{% for plugin in plugins.keys()|sort() %}
|
|
||||||
{% with configuration=plugins[plugin], utils=utils %}
|
|
||||||
<div class="tab-pane plugin-tab-content" id="{% print plugin %}">
|
|
||||||
{% include 'plugins/' + plugin + '.html' %}
|
|
||||||
</div>
|
|
||||||
{% endwith %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="notification-container"></div>
|
|
||||||
|
|
||||||
<div id="hidden-plugins-container">
|
|
||||||
{% for plugin in hidden_plugins.keys()|sort() %}
|
|
||||||
{% set configuration = plugins[plugin] %}
|
|
||||||
<div class="plugin" id="{% print plugin %}">
|
|
||||||
{% include 'plugins/' + plugin + '.html' %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
|
|
20
platypush/backend/http/templates/nav.html
Normal file
20
platypush/backend/http/templates/nav.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<nav>
|
||||||
|
<ul class="row">
|
||||||
|
{% for plugin in plugins|sort %}
|
||||||
|
<li :class="{selected: '{{ plugin }}' == selectedPlugin}">
|
||||||
|
<a href="#{{ plugin }}" @click="selectedPlugin = '{{ plugin }}'">
|
||||||
|
{{ plugin }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<span class="decorator" v-if="'{{ plugin }}' == selectedPlugin"></span>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="#{{ plugin }}">Test tab 2</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#{{ plugin }}">Test tab 3</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
IT WORKED!
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script type="text/x-template" id="tmpl-light-hue-property-selector">
|
||||||
|
<div class="properties color-properties">
|
||||||
|
<div class="row slider-container red-properties" v-if="value.xy">
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="color-logo color-logo-red"></div>
|
||||||
|
</div>
|
||||||
|
<div class="slider-container col-10">
|
||||||
|
<input class="slider red" type="range" min="0" max="255" v-model="rgb[0]" @change="changed">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row slider-container green-properties" v-if="value.xy">
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="color-logo color-logo-green"></div>
|
||||||
|
</div>
|
||||||
|
<div class="slider-container col-10">
|
||||||
|
<input class="slider green" type="range" min="0" max="255" v-model="rgb[1]" @change="changed">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row slider-container blue-properties" v-if="value.xy">
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="color-logo color-logo-blue"></div>
|
||||||
|
</div>
|
||||||
|
<div class="slider-container col-10">
|
||||||
|
<input class="slider blue" type="range" min="0" max="255" v-model="rgb[2]" @change="changed">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row slider-container bri-properties" v-if="value.bri !== undefined">
|
||||||
|
<div class="col-2">
|
||||||
|
<i class="fa fa-lightbulb-o"></i>
|
||||||
|
</div>
|
||||||
|
<div class="slider-container col-10">
|
||||||
|
<input class="slider bri" type="range" min="0" max="255" v-model="value.bri" @change="changed">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row slider-container ct-properties" v-if="value.ct !== undefined">
|
||||||
|
<div class="col-2">
|
||||||
|
<i class="fa fa-thermometer"></i>
|
||||||
|
</div>
|
||||||
|
<div class="slider-container col-10">
|
||||||
|
<input class="slider ct" type="range" min="0" max="255" v-model="value.ct" @change="changed">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/light.hue/elements.js') }}"></script>
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<script type="text/x-template" id="tmpl-light-hue-group">
|
||||||
|
<div class="group">
|
||||||
|
<div v-text="name" @click="$emit('set-selected')"></div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/x-template" id="tmpl-light-hue-group-controller">
|
||||||
|
<div class="group-controller">
|
||||||
|
<div class="row vertical-center">
|
||||||
|
<div class="col-10" @click="propertiesCollapsedToggled">All Lights</div>
|
||||||
|
<div class="col-2 pull-right">
|
||||||
|
<toggle-switch v-model="value.state.any_on" :glow="true" @toggled="toggled"></toggle-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<light-hue-property-selector
|
||||||
|
v-model="properties"
|
||||||
|
:class="{hidden: collapsed}"
|
||||||
|
@bri-changed="briChanged"
|
||||||
|
@color-changed="colorChanged"
|
||||||
|
@ct-changed="ctChanged">
|
||||||
|
</light-hue-property-selector>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/light.hue/groups.js') }}"></script>
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/light.hue/converter.js') }}"></script>
|
||||||
|
|
||||||
|
{% include 'plugins/light.hue/elements.html' %}
|
||||||
|
{% include 'plugins/light.hue/groups.html' %}
|
||||||
|
{% include 'plugins/light.hue/scenes.html' %}
|
||||||
|
{% include 'plugins/light.hue/units.html' %}
|
||||||
|
|
||||||
|
<script type="text/x-template" id="tmpl-light-hue">
|
||||||
|
<div class="row light-hue-container">
|
||||||
|
<div class="groups col-no-margin-3 col-s-12">
|
||||||
|
<div class="title">Rooms</div>
|
||||||
|
<light-hue-group
|
||||||
|
v-for="(group, id) in groups"
|
||||||
|
:key="id"
|
||||||
|
:id="id"
|
||||||
|
:name="group.name"
|
||||||
|
:class="{selected: selectedGroup == id}"
|
||||||
|
@set-selected="selectedGroup = id">
|
||||||
|
</light-hue-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="scenes col-no-margin-3 col-s-12">
|
||||||
|
<div class="title">Scenes</div>
|
||||||
|
<light-hue-scene
|
||||||
|
v-for="(scene, id) in scenes"
|
||||||
|
:key="id"
|
||||||
|
:id="id"
|
||||||
|
:name="scene.name"
|
||||||
|
:class="{hidden: !(selectedGroup in scene.groups), selected: selectedScene == id}"
|
||||||
|
@input="selectScene">
|
||||||
|
</light-hue-scene>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="units col-no-margin-6 col-s-12">
|
||||||
|
<div class="title">Lights</div>
|
||||||
|
<light-hue-group-controller
|
||||||
|
v-if="selectedGroup"
|
||||||
|
v-model="groups[selectedGroup]"
|
||||||
|
:id="selectedGroup"
|
||||||
|
:groups="groups"
|
||||||
|
:collapsed="!(selectedProperties.type == 'group' && selectedProperties.id == selectedGroup)"
|
||||||
|
@properties-collapsed-toggled="collapsedToggled"
|
||||||
|
@input="updatedGroup">
|
||||||
|
</light-hue-group-controller>
|
||||||
|
|
||||||
|
<light-hue-unit
|
||||||
|
v-for="(light, id) in lights"
|
||||||
|
v-model="light.state"
|
||||||
|
:key="id"
|
||||||
|
:id="id"
|
||||||
|
:name="light.name"
|
||||||
|
:config="light.config"
|
||||||
|
:capabilities="light.capabilities"
|
||||||
|
:modelid="light.modelid"
|
||||||
|
:manufacturername="light.manufacturername"
|
||||||
|
:swupdate="light.swupdate"
|
||||||
|
:swversion="light.swversion"
|
||||||
|
:uniqueid="light.uniqueid"
|
||||||
|
:type="light.type"
|
||||||
|
:productname="light.productname"
|
||||||
|
:collapsed="!(selectedProperties.type == 'unit' && selectedProperties.id == id)"
|
||||||
|
:class="{hidden: !(selectedGroup in light.groups)}"
|
||||||
|
@input="onUnitInput"
|
||||||
|
@properties-collapsed-toggled="collapsedToggled">
|
||||||
|
</light-hue-unit>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<script type="text/x-template" id="tmpl-light-hue-scene">
|
||||||
|
<div class="scene" @click="clicked">
|
||||||
|
<div v-text="name"></div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/light.hue/scenes.js') }}"></script>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script type="text/x-template" id="tmpl-light-hue-unit">
|
||||||
|
<div class="unit">
|
||||||
|
<div class="row vertical-center">
|
||||||
|
<div class="name col-10" v-text="name" @click="propertiesCollapsedToggled"></div>
|
||||||
|
<div class="col-2 pull-right">
|
||||||
|
<toggle-switch v-model="value.on" @toggled="toggled"></toggle-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<light-hue-property-selector
|
||||||
|
v-model="value"
|
||||||
|
:class="{hidden: collapsed}"
|
||||||
|
@bri-changed="briChanged"
|
||||||
|
@color-changed="colorChanged"
|
||||||
|
@ct-changed="ctChanged">
|
||||||
|
</light-hue-property-selector>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/light.hue/units.js') }}"></script>
|
||||||
|
|
6
platypush/backend/http/templates/plugins/template.html
Normal file
6
platypush/backend/http/templates/plugins/template.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<script type="text/x-template" id="tmpl-plugin">
|
||||||
|
<div class="plugin-container" :class="{selected: selected}">
|
||||||
|
<component :is="tag" :config="config"/>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
|
@ -96,5 +96,9 @@ class HttpUtils(object):
|
||||||
def get_config(cls, attr):
|
def get_config(cls, attr):
|
||||||
return Config.get(attr)
|
return Config.get(attr)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def plugin_name_to_tag(cls, module_name):
|
||||||
|
return module_name.replace('.','-')
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -135,3 +135,6 @@ websocket-client
|
||||||
# mpv player plugin
|
# mpv player plugin
|
||||||
python-mpv
|
python-mpv
|
||||||
|
|
||||||
|
# SCSS/SASS to CSS compiler for web pages style
|
||||||
|
pyScss
|
||||||
|
|
||||||
|
|
74
setup.py
74
setup.py
|
@ -2,10 +2,67 @@
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
|
import distutils.cmd
|
||||||
|
from distutils.command.build import build
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
def read(fname):
|
|
||||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
class WebBuildCommand(distutils.cmd.Command):
|
||||||
|
"""
|
||||||
|
Custom command to build the web files
|
||||||
|
"""
|
||||||
|
|
||||||
|
description = 'Build components and styles for the web pages'
|
||||||
|
user_options = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_css_files():
|
||||||
|
from scss import Compiler
|
||||||
|
|
||||||
|
print('Building CSS files')
|
||||||
|
base_path = path(os.path.join('platypush','backend','http','static','css'))
|
||||||
|
input_path = path(os.path.join(base_path,'source'))
|
||||||
|
output_path = path(os.path.join(base_path,'dist'))
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(input_path):
|
||||||
|
scss_file = os.path.join(root, 'index.scss')
|
||||||
|
if os.path.isfile(scss_file):
|
||||||
|
css_path = os.path.split(scss_file[len(input_path):])[0][1:] + '.css'
|
||||||
|
css_dir = os.path.join(output_path, os.path.dirname(css_path))
|
||||||
|
css_file = os.path.join(css_dir, os.path.basename(css_path))
|
||||||
|
|
||||||
|
os.makedirs(css_dir, exist_ok=True)
|
||||||
|
print('\tGenerating CSS {scss} -> {css}'.format(scss=scss_file, css=css_file))
|
||||||
|
|
||||||
|
with open(css_file, 'w') as f:
|
||||||
|
css_content = Compiler(output_style='compressed', search_path=[root, input_path]).compile(scss_file)
|
||||||
|
f.write(css_content)
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.generate_css_files()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BuildCommand(build):
|
||||||
|
def run(self):
|
||||||
|
build.run(self)
|
||||||
|
self.run_command('web_build')
|
||||||
|
|
||||||
|
|
||||||
|
def path(fname=''):
|
||||||
|
return os.path.abspath(os.path.join(os.path.dirname(__file__), fname))
|
||||||
|
|
||||||
|
|
||||||
|
def readfile(fname):
|
||||||
|
with open(path(fname)) as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
def pkg_files(dir):
|
def pkg_files(dir):
|
||||||
paths = []
|
paths = []
|
||||||
|
@ -14,6 +71,7 @@ def pkg_files(dir):
|
||||||
paths.append(os.path.join('..', path, file))
|
paths.append(os.path.join('..', path, file))
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
|
||||||
def create_etc_dir():
|
def create_etc_dir():
|
||||||
path = '/etc/platypush'
|
path = '/etc/platypush'
|
||||||
try:
|
try:
|
||||||
|
@ -26,6 +84,7 @@ def create_etc_dir():
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
plugins = pkg_files('platypush/plugins')
|
plugins = pkg_files('platypush/plugins')
|
||||||
backend = pkg_files('platypush/backend')
|
backend = pkg_files('platypush/backend')
|
||||||
# create_etc_dir()
|
# create_etc_dir()
|
||||||
|
@ -37,8 +96,8 @@ setup(
|
||||||
author_email = "info@fabiomanganiello.com",
|
author_email = "info@fabiomanganiello.com",
|
||||||
description = ("Platypush service"),
|
description = ("Platypush service"),
|
||||||
license = "MIT",
|
license = "MIT",
|
||||||
python_requires = '>= 3',
|
python_requires = '>= 3.5',
|
||||||
keywords = "pushbullet notifications automation",
|
keywords = "home-automation iot mqtt websockets redis dashboard notificaions",
|
||||||
url = "https://github.com/BlackLight/platypush",
|
url = "https://github.com/BlackLight/platypush",
|
||||||
packages = find_packages(),
|
packages = find_packages(),
|
||||||
include_package_data = True,
|
include_package_data = True,
|
||||||
|
@ -50,10 +109,14 @@ setup(
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
scripts = ['bin/platyvenv'],
|
scripts = ['bin/platyvenv'],
|
||||||
|
cmdclass = {
|
||||||
|
'web_build': WebBuildCommand,
|
||||||
|
'build': BuildCommand,
|
||||||
|
},
|
||||||
# data_files = [
|
# data_files = [
|
||||||
# ('/etc/platypush', ['platypush/config.example.yaml'])
|
# ('/etc/platypush', ['platypush/config.example.yaml'])
|
||||||
# ],
|
# ],
|
||||||
long_description = read('README.md'),
|
long_description = readfile('README.md'),
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Topic :: Utilities",
|
"Topic :: Utilities",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
|
@ -99,6 +162,7 @@ setup(
|
||||||
'Support for web media subtitles': ['webvtt-py'],
|
'Support for web media subtitles': ['webvtt-py'],
|
||||||
'Support for mopidy backend': ['websocket-client'],
|
'Support for mopidy backend': ['websocket-client'],
|
||||||
'Support for mpv player plugin': ['python-mpv'],
|
'Support for mpv player plugin': ['python-mpv'],
|
||||||
|
'Support for compiling SASS/SCSS styles to CSS': ['pyScss'],
|
||||||
# 'Support for Leap Motion backend': ['git+ssh://git@github.com:BlackLight/leap-sdk-python3.git'],
|
# 'Support for Leap Motion backend': ['git+ssh://git@github.com:BlackLight/leap-sdk-python3.git'],
|
||||||
# 'Support for Flic buttons': ['git+https://@github.com/50ButtonsEach/fliclib-linux-hci.git']
|
# 'Support for Flic buttons': ['git+https://@github.com/50ButtonsEach/fliclib-linux-hci.git']
|
||||||
# 'Support for media subtitles': ['git+https://github.com/agonzalezro/python-opensubtitles#egg=python-opensubtitles']
|
# 'Support for media subtitles': ['git+https://github.com/agonzalezro/python-opensubtitles#egg=python-opensubtitles']
|
||||||
|
|
Loading…
Reference in a new issue