diff --git a/homie-esp8266-v1-setup-sources/.babelrc b/homie-esp8266-v1-setup-sources/.babelrc new file mode 100644 index 0000000..11f1df8 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "stage-3"] +} diff --git a/homie-esp8266-v1-setup-sources/.gitignore b/homie-esp8266-v1-setup-sources/.gitignore new file mode 100644 index 0000000..580a5df --- /dev/null +++ b/homie-esp8266-v1-setup-sources/.gitignore @@ -0,0 +1,5 @@ +/node_modules/ +/public/ + +/npm-debug.log +/ui_bundle.gz diff --git a/homie-esp8266-v1-setup-sources/LICENSE b/homie-esp8266-v1-setup-sources/LICENSE new file mode 100644 index 0000000..22fbe5d --- /dev/null +++ b/homie-esp8266-v1-setup-sources/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file diff --git a/homie-esp8266-v1-setup-sources/README.md b/homie-esp8266-v1-setup-sources/README.md new file mode 100644 index 0000000..2f47e5c --- /dev/null +++ b/homie-esp8266-v1-setup-sources/README.md @@ -0,0 +1,11 @@ +Configurator for Homie for ESP8266 +================================== + +Sources of the UI to configure an ESP8266 loaded with an Homie firmware. + +## Contribute + +Contributions are very welcome! + +To build assets, just run `npm run dev`. +This will build the public directory, and watch for changes in the `app` folder. diff --git a/homie-esp8266-v1-setup-sources/app/assets/favicon.ico b/homie-esp8266-v1-setup-sources/app/assets/favicon.ico new file mode 100644 index 0000000..24b7795 Binary files /dev/null and b/homie-esp8266-v1-setup-sources/app/assets/favicon.ico differ diff --git a/homie-esp8266-v1-setup-sources/app/assets/img/favicon-16x16.png b/homie-esp8266-v1-setup-sources/app/assets/img/favicon-16x16.png new file mode 100644 index 0000000..c0de481 Binary files /dev/null and b/homie-esp8266-v1-setup-sources/app/assets/img/favicon-16x16.png differ diff --git a/homie-esp8266-v1-setup-sources/app/assets/img/favicon-192x192.png b/homie-esp8266-v1-setup-sources/app/assets/img/favicon-192x192.png new file mode 100644 index 0000000..3de1566 Binary files /dev/null and b/homie-esp8266-v1-setup-sources/app/assets/img/favicon-192x192.png differ diff --git a/homie-esp8266-v1-setup-sources/app/assets/img/favicon-32x32.png b/homie-esp8266-v1-setup-sources/app/assets/img/favicon-32x32.png new file mode 100644 index 0000000..282467e Binary files /dev/null and b/homie-esp8266-v1-setup-sources/app/assets/img/favicon-32x32.png differ diff --git a/homie-esp8266-v1-setup-sources/app/assets/img/logo.png b/homie-esp8266-v1-setup-sources/app/assets/img/logo.png new file mode 100644 index 0000000..72c360d Binary files /dev/null and b/homie-esp8266-v1-setup-sources/app/assets/img/logo.png differ diff --git a/homie-esp8266-v1-setup-sources/app/js/app.js b/homie-esp8266-v1-setup-sources/app/js/app.js new file mode 100644 index 0000000..3c83b4f --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/js/app.js @@ -0,0 +1,140 @@ +'use strict'; + +import 'whatwg-fetch'; + +import React from 'react'; +import ReactDOM from 'react-dom'; + +import ConnectionStep from './steps/connection'; +import InfoStep from './steps/info'; +import WifiStep from './steps/wifi'; +import MqttStep from './steps/mqtt'; +import DetailsStep from './steps/details'; +import SendingStep from './steps/sending'; + +const BASE_API = 'http://homie.config'; + +const STEP_CONNECTION = 1; +const STEP_INFO = 2; +const STEP_WIFI = 3; +const STEP_MQTT = 4; +const STEP_DETAILS = 5; +const STEP_SENDING = 6; + +class App extends React.Component { + constructor (props) { + super(props); + this.state = { + step: STEP_CONNECTION, + name: null, + deviceId: null, + wifi: { }, + mqtt: { }, + ota: { } + }; + } + + nextStep () { + this.setState({ step: this.state.step + 1 }); + } + + setName (name) { + this.setState({ name }); + } + + setDeviceId (deviceId) { + this.setState({ deviceId }); + } + + setWifiCreds (creds) { + this.setState({ wifi: creds }); + } + + setMqttCreds (creds) { + this.setState({ mqtt: creds }); + } + + setOtaCreds (creds) { + this.setState({ ota: creds }); + } + + sendConfig () { + let body = { + name: this.state.name, + wifi: this.state.wifi, + mqtt: this.state.mqtt, + ota: this.state.ota + }; + + if (this.state.deviceId !== null) body['device_id'] = this.state.deviceId; + + let options = { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }; + + return new Promise((resolve, reject) => { + window.fetch(`${BASE_API}/config`, options).then((res) => { + return res.json(); + }).then((json) => { + if (json.success) { + resolve(); + } else { + reject(); + } + }).catch(() => { + reject(); + }); + }); + } + + render () { + let Step; + + switch (this.state.step) { + case STEP_CONNECTION: + Step = ConnectionStep; + break; + case STEP_INFO: + Step = InfoStep; + break; + case STEP_WIFI: + Step = WifiStep; + break; + case STEP_MQTT: + Step = MqttStep; + break; + case STEP_DETAILS: + Step = DetailsStep; + break; + case STEP_SENDING: + Step = SendingStep; + break; + } + + return ( +
+

Homie for ESP8266

+

Set up your device.

+ +
+ +
+ + this.nextStep()} setWifiCreds={(creds) => this.setWifiCreds(creds)} setMqttCreds={(creds) => this.setMqttCreds(creds)} setName={(name) => this.setName(name)} setDeviceId={(deviceId) => this.setDeviceId(deviceId)} setOtaCreds={(creds) => this.setOtaCreds(creds)} sendConfig={() => this.sendConfig()} /> +
+ ); + } +} + +ReactDOM.render(, document.getElementById('app')); diff --git a/homie-esp8266-v1-setup-sources/app/js/steps/connection.js b/homie-esp8266-v1-setup-sources/app/js/steps/connection.js new file mode 100644 index 0000000..02ff23d --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/js/steps/connection.js @@ -0,0 +1,53 @@ +'use strict'; + +import 'whatwg-fetch'; + +import React from 'react'; + +export default class ConnectionStep extends React.Component { + constructor (props) { + super(props); + } + + componentDidMount () { + let interval; + let done = false; + let heartbeat = () => { + window.fetch(`${this.props.baseApi}/heart`).then((res) => { + if (res.ok && !done) { + done = true; // prevent multiple request to trigger multiple nextStep + window.clearInterval(interval); + this.props.nextStep(); + } + }); + }; + + interval = window.setInterval(heartbeat, 5 * 1000); + heartbeat(); + } + + render () { + return ( +
+
+

+ Connect to your device Wi-Fi AP. + If the AP is named Homie-1234abcd, + the password is 1234abcd. +

+
+
+ Loading + Waiting for the device... +
+
+ ); + } +} + +if (process.env.NODE_ENV !== 'production') { // for Preact + ConnectionStep.propTypes = { + baseApi: React.PropTypes.string.isRequired, + nextStep: React.PropTypes.func.isRequired + }; +} diff --git a/homie-esp8266-v1-setup-sources/app/js/steps/details.js b/homie-esp8266-v1-setup-sources/app/js/steps/details.js new file mode 100644 index 0000000..bf985c0 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/js/steps/details.js @@ -0,0 +1,144 @@ +'use strict'; + +import React from 'react'; + +export default class DetailsStep extends React.Component { + constructor (props) { + super(props); + + this.state = { + ota: false, + ssl: false + }; + } + + handleOtaCheckbox (e) { + this.setState({ ota: e.target.checked }); + } + + handleSslCheckbox (e) { + this.setState({ ssl: e.target.checked }); + } + + handleFormSubmit (e) { + e.preventDefault(); + + let otaCreds = {}; + otaCreds.enabled = false; + + if (this.state.ota) { + otaCreds.enabled = true; + + otaCreds.host = this.refs.host.value; + otaCreds.port = parseInt(this.refs.port.value, 10); + + otaCreds.ssl = false; + if (this.state.ssl) { + otaCreds.ssl = true; + if (this.refs.fingerprint.value !== '') otaCreds['fingerprint'] = this.refs.fingerprint.value; + } + + otaCreds.path = this.refs.path.value; + } + + this.props.setName(this.refs.name.value); + if (this.refs.deviceId.value !== '') this.props.setDeviceId(this.refs.deviceId.value); + this.props.setOtaCreds(otaCreds); + + this.props.nextStep(); + } + + render () { + return ( +
+

+ A few details before finishing the configuration. +

+ +
this.handleFormSubmit(e) }> + +

+ + Required. +

+ + +

+ + Optional. The default value is the hardware device ID. MAY be composed of lowercase letters from a to z, numbers from 0 to 9, and it MAY contain -, but MUST NOT start or end with a - +

+ +

+ +

+ + {(() => { + if (this.state.ota) { + return ( +
+ +

+ + Required. +

+ + +

+ + Required. +

+ +

+ +

+ + {(() => { + if (this.state.ssl) { + return ( +
+ +

+ + Optional. Can be lower-case, upper-case, separated by spaced or :. +

+
+ ); + } + })()} + + +

+ + Required. +

+ +
+
+ ); + } + })()} + +

+ +

+
+
+ ); + } +} + +if (process.env.NODE_ENV !== 'production') { // for Preact + DetailsStep.propTypes = { + nextStep: React.PropTypes.func.isRequired, + mqttConfig: React.PropTypes.object.isRequired, + setName: React.PropTypes.func.isRequired, + setDeviceId: React.PropTypes.func.isRequired, + setOtaCreds: React.PropTypes.func.isRequired + }; +} diff --git a/homie-esp8266-v1-setup-sources/app/js/steps/info.js b/homie-esp8266-v1-setup-sources/app/js/steps/info.js new file mode 100644 index 0000000..4788011 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/js/steps/info.js @@ -0,0 +1,105 @@ +'use strict'; + +import 'whatwg-fetch'; + +import React from 'react'; + +export default class InfoStep extends React.Component { + constructor (props) { + super(props); + this.state = { + loading: true, + info: {} + }; + } + + componentDidMount () { + let interval; + let done = false; + let deviceinfo = () => { + window.fetch(`${this.props.baseApi}/device-info`).then((res) => { + if (res.ok && !done) { + done = true; + window.clearInterval(interval); + return res.json(); + } + }).then((json) => { + this.setState({ + loading: false, + info: json + }); + }); + }; + + interval = window.setInterval(deviceinfo, 5 * 1000); + deviceinfo(); + } + + handleNextButton (e) { + e.preventDefault(); + + this.props.nextStep(); + } + + render () { + return ( +
+ {(() => { + if (this.state.loading) { + return ( +
+ Loading + Gathering device information... +
+ ); + } else { + return ( +
+

+ Here are some information about the device you are about to configure: +

+ +
    +
  • Device ID: { this.state.info.device_id }
  • +
  • Homie version: { this.state.info.homie_version }
  • +
  • Firmware name: { this.state.info.firmware.name }
  • +
  • Firmware version: { this.state.info.firmware.version }
  • +
  • + Nodes: + + + + + + + + + { this.state.info.nodes.map((node, i) => { + return ( + + + + + ); + }) } + +
    IDType
    { node.id }{ node.type }
    +
  • +
+ + { this.handleNextButton(e); } }>Next +
+ ); + } + })()} +
+ ); + } +} + +if (process.env.NODE_ENV !== 'production') { // for Preact + InfoStep.propTypes = { + baseApi: React.PropTypes.string.isRequired, + nextStep: React.PropTypes.func.isRequired + }; +} diff --git a/homie-esp8266-v1-setup-sources/app/js/steps/mqtt.js b/homie-esp8266-v1-setup-sources/app/js/steps/mqtt.js new file mode 100644 index 0000000..dda1699 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/js/steps/mqtt.js @@ -0,0 +1,146 @@ +'use strict'; + +import React from 'react'; + +export default class MqttStep extends React.Component { + constructor (props) { + super(props); + this.state = { + auth: false, + showPassword: false, + ssl: false + }; + } + + handleSslCheckbox (e) { + this.setState({ ssl: e.target.checked }); + } + + handleAuthCheckbox (e) { + this.setState({ auth: e.target.checked }); + } + + handleHiddenCheckbox (e) { + this.setState({ showPassword: e.target.checked }); + } + + handleFormSubmit (e) { + e.preventDefault(); + + let creds = {}; + creds.host = this.refs.host.value; + creds.port = parseInt(this.refs.port.value, 10); + if (this.refs.baseTopic.value !== '') creds['base_topic'] = this.refs.baseTopic.value; + + creds.ssl = false; + if (this.state.ssl) { + creds.ssl = true; + if (this.refs.fingerprint.value !== '') creds['fingerprint'] = this.refs.fingerprint.value; + } + + creds.auth = false; + if (this.state.auth) { + creds.auth = true; + creds.username = this.refs.username.value; + creds.password = this.refs.password.value; + } + + this.props.setMqttCreds(creds); + this.props.nextStep(); + } + + render () { + return ( +
+

+ Enter the MQTT credentials. +

+ +
this.handleFormSubmit(e) }> + +

+ + Required. +

+ + +

+ + Required. +

+ + +

+ + Optional. The default value is devices/. +

+ +

+ +

+ + {(() => { + if (this.state.ssl) { + return ( +
+ +

+ + Optional. Can be lower-case, upper-case, separated by spaced or :. +

+
+ ); + } + })()} + +

+ +

+ + {(() => { + if (this.state.auth) { + return ( +
+ +

+ + Required. +

+ + +

+ + +

+ Required. + +
+
+ ); + } + })()} + +

+ +

+
+
+ ); + } +} + +if (process.env.NODE_ENV !== 'production') { // for Preact + MqttStep.propTypes = { + nextStep: React.PropTypes.func.isRequired, + setMqttCreds: React.PropTypes.func.isRequired + }; +} diff --git a/homie-esp8266-v1-setup-sources/app/js/steps/sending.js b/homie-esp8266-v1-setup-sources/app/js/steps/sending.js new file mode 100644 index 0000000..4bd41b6 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/js/steps/sending.js @@ -0,0 +1,58 @@ +'use strict'; + +import React from 'react'; + +export default class SendingStep extends React.Component { + constructor (props) { + super(props); + this.state = { + loading: true, + success: null + }; + } + + componentDidMount () { + this.props.sendConfig().then(() => { + this.setState({ loading: false, success: true }); + }).catch(() => { + this.setState({ loading: false, success: false }); + }); + } + + render () { + return ( +
+ {(() => { + if (this.state.loading) { + return ( +
+ Loading + Sending configuration... +
+ ); + } else { + if (this.state.success) { + return ( +
+ The configuration was sent. Your device will reboot. +
+ ); + } else { + return ( +
+ There was an error while sending the configuration. Please retry. +
+ ); + } + } + })()} +
+ ); + } +} + +if (process.env.NODE_ENV !== 'production') { // for Preact + SendingStep.propTypes = { + sendConfig: React.PropTypes.func.isRequired + }; +} diff --git a/homie-esp8266-v1-setup-sources/app/js/steps/wifi.js b/homie-esp8266-v1-setup-sources/app/js/steps/wifi.js new file mode 100644 index 0000000..49d12c1 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/js/steps/wifi.js @@ -0,0 +1,202 @@ +'use strict'; + +import 'whatwg-fetch'; + +import React from 'react'; + +export default class WifiStep extends React.Component { + constructor (props) { + super(props); + this.state = { + loading: false, + networks: { networks: [] }, + buttonDisabled: true, + selectedSsid: null, + showSsidInput: false, + showPasswordInput: false, + showPassword: false + }; + } + + componentDidMount () { + let interval; + let done = false; + let networks = () => { + window.fetch(`${this.props.baseApi}/networks`).then((res) => { + if (res.ok && !done) { + done = true; + window.clearInterval(interval); + return res.json(); + } + }).then((json) => { + this.setState({ + loading: false, + networks: json + }); + }); + }; + + interval = window.setInterval(networks, 5 * 1000); + networks(); + } + + handleSelectChange (e) { + if (e.target.value === 'select') { + this.setState({ showSsidInput: false, showPasswordInput: false, selectedSsid: null, buttonDisabled: true }); + } else if (e.target.value === 'other') { + this.setState({ showSsidInput: true, showPasswordInput: true, selectedSsid: null, buttonDisabled: false }); + } else { + let data = e.target.options[e.target.selectedIndex].dataset; + this.setState({ showSsidInput: false, showPasswordInput: data.open === 'no', selectedSsid: data.ssid, buttonDisabled: false }); + } + } + + handleHiddenChange (e) { + this.setState({ showPassword: e.target.checked }); + } + + handleFormSubmit (e) { + e.preventDefault(); + + let creds = {}; + + if (this.state.selectedSsid) { + creds.ssid = this.state.selectedSsid; + } else { + creds.ssid = this.refs.ssid.value; + } + creds.password = this.refs.password.value; + + this.props.setWifiCreds(creds); + this.props.nextStep(); + } + + render () { + return ( +
+ {(() => { + if (this.state.loading) { + return ( +
+ Loading + Gathering available networks... +
+ ); + } else { + this.state.networks.networks.sort(function (networkA, networkB) { + if (networkA.rssi > networkB.rssi) { + return -1; + } else if (networkA.rssi < networkB.rssi) { + return 1; + } else { + return 0; + } + }); + + let networks = this.state.networks.networks.map(function (network) { + if (network.rssi <= -100) { + network.signalQuality = 0; + } else if (network.rssi >= -50) { + network.signalQuality = 100; + } else { + network.signalQuality = 2 * (network.rssi + 100); + } + + switch (network.encryption) { + case 'wep': + network.encryption = 'WEP'; + break; + case 'wpa': + network.encryption = 'WPA'; + break; + case 'wpa2': + network.encryption = 'WPA2'; + break; + case 'none': + network.encryption = 'Open'; + break; + case 'auto': + network.encryption = 'Automatic'; + break; + } + return network; + }); + + return ( +
+

+ Select the Wi-Fi network to connect to: +

+ +
this.handleFormSubmit(e) }> + +

+ + + +

+ + {(() => { + if (this.state.showSsidInput) { + return ( +
+ +

+ + Required. +

+
+ ); + } + })()} + + {(() => { + if (this.state.showPasswordInput) { + return ( +
+ +

+ + +

+ Optional. Leave blank if open network. +
+
+ ); + } + })()} + +

+ +

+
+
+ ); + } + })()} +
+ ); + } +} + +if (process.env.NODE_ENV !== 'production') { // for Preact + WifiStep.propTypes = { + baseApi: React.PropTypes.string.isRequired, + nextStep: React.PropTypes.func.isRequired, + setWifiCreds: React.PropTypes.func.isRequired + }; +} diff --git a/homie-esp8266-v1-setup-sources/app/vendor/bulma/css/bulma.min.css b/homie-esp8266-v1-setup-sources/app/vendor/bulma/css/bulma.min.css new file mode 100644 index 0000000..d5d5466 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/vendor/bulma/css/bulma.min.css @@ -0,0 +1,2 @@ +html,body,body div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,figure,footer,header,menu,nav,section,time,mark,audio,video,details,summary{margin:0;padding:0;border:0;font-size:100%;font-weight:normal;vertical-align:baseline;background:transparent}article,aside,figure,footer,header,nav,section,details,summary{display:block}html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}img,object,embed{max-width:100%}html{overflow-y:scroll}ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}a{margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent}del{text-decoration:line-through}abbr[title],dfn[title]{border-bottom:1px dotted #000;cursor:help}table{border-collapse:collapse;border-spacing:0}th{font-weight:bold;vertical-align:bottom}td{font-weight:normal;vertical-align:top}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}input,select{vertical-align:middle}pre{white-space:pre;white-space:pre-wrap;white-space:pre-line;word-wrap:break-word}input[type="radio"]{vertical-align:text-bottom}input[type="checkbox"]{vertical-align:bottom}select,input,textarea{font:99% sans-serif}table{font-size:inherit;font:100%}small{font-size:85%}strong{font-weight:bold}td,td img{vertical-align:top}sub,sup{font-size:75%;line-height:0;position:relative}sup{top:-0.5em}sub{bottom:-0.25em}pre,code,kbd,samp{font-family:monospace, sans-serif}label,input[type=button],input[type=submit],input[type=file],button{cursor:pointer}button,input,select,textarea{margin:0}button,input[type=button]{width:auto;overflow:visible}@-webkit-keyframes spin-around{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin-around{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}html{background:#f5f7fa;font-size:14px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility}html.has-modal-open{overflow:hidden}body,button,input,select,textarea{font-family:"Helvetica Neue","Helvetica","Arial",sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace;line-height:1.25}body{color:#69707a;font-size:1rem;line-height:1.428571428571429}a{color:#1fc8db;cursor:pointer;text-decoration:none;-webkit-transition:none 86ms ease-out;transition:none 86ms ease-out}a:hover{color:#222324}code{background:#f5f7fa;color:#ed6c63;font-size:12px;font-weight:normal;padding:1px 2px 2px}hr{border-top-color:#d3d6db;margin:20px 0}img{max-width:100%}input[type="checkbox"],input[type="radio"]{vertical-align:baseline}small{font-size:11px}strong{color:#222324}article,aside,figure,footer,header,hgroup,section{display:block}pre{background:#f5f7fa;color:#69707a;white-space:pre;word-wrap:normal}pre code{background:#f5f7fa;color:#69707a;display:block;overflow-x:auto;padding:16px 20px}table{width:100%}table th,table td{text-align:left;vertical-align:top}table th{color:#222324}.block:not(:last-child),.content:not(:last-child),.title:not(:last-child),.subtitle:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.progress:not(:last-child),.highlight:not(:last-child),.navbar:not(:last-child),.tabs:not(:last-child),.box:not(:last-child){margin-bottom:20px}.container{position:relative}@media screen and (min-width: 980px){.container{margin:0 auto;max-width:960px}.container.is-fluid{margin:0 20px;max-width:none}}.fa{font-size:21px;text-align:center;vertical-align:top}.content.is-medium{font-size:18px}.content.is-medium code{font-size:14px}.content.is-large{font-size:24px}.content.is-large code{font-size:18px}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#222324;font-weight:300;line-height:1.125;margin-bottom:20px}.content h1:not(:first-child),.content h2:not(:first-child),.content h3:not(:first-child){margin-top:40px}.content h1{font-size:2em}.content h2{font-size:1.75em}.content h3{font-size:1.5em}.content h4{font-size:1.25em}.content h5{font-size:1.125em}.content h6{font-size:1em}.content p:not(:last-child){margin-bottom:1em}.content li+li{margin-top:0.25em}.content ol{list-style:decimal outside;margin:1em 2em}.content ul{list-style:disc outside;margin:1em 2em}.content ul ul{list-style-type:circle;margin-top:0.5em}.content ul ul ul{list-style-type:square}.content blockquote{background:#f5f7fa;border-left:5px solid #d3d6db;padding:1.5em}.content blockquote:not(:last-child){margin-bottom:1em}.highlight{background-color:#fdf6e3;color:#586e75}.highlight .c{color:#93a1a1}.highlight .err,.highlight .g{color:#586e75}.highlight .k{color:#859900}.highlight .l,.highlight .n{color:#586e75}.highlight .o{color:#859900}.highlight .x{color:#cb4b16}.highlight .p{color:#586e75}.highlight .cm{color:#93a1a1}.highlight .cp{color:#859900}.highlight .c1{color:#93a1a1}.highlight .cs{color:#859900}.highlight .gd{color:#2aa198}.highlight .ge{color:#586e75;font-style:italic}.highlight .gr{color:#dc322f}.highlight .gh{color:#cb4b16}.highlight .gi{color:#859900}.highlight .go,.highlight .gp{color:#586e75}.highlight .gs{color:#586e75;font-weight:bold}.highlight .gu{color:#cb4b16}.highlight .gt{color:#586e75}.highlight .kc{color:#cb4b16}.highlight .kd{color:#268bd2}.highlight .kn,.highlight .kp{color:#859900}.highlight .kr{color:#268bd2}.highlight .kt{color:#dc322f}.highlight .ld{color:#586e75}.highlight .m,.highlight .s{color:#2aa198}.highlight .na{color:#B58900}.highlight .nb{color:#586e75}.highlight .nc{color:#268bd2}.highlight .no{color:#cb4b16}.highlight .nd{color:#268bd2}.highlight .ni,.highlight .ne{color:#cb4b16}.highlight .nf{color:#268bd2}.highlight .nl,.highlight .nn,.highlight .nx,.highlight .py{color:#586e75}.highlight .nt,.highlight .nv{color:#268bd2}.highlight .ow{color:#859900}.highlight .w{color:#586e75}.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:#2aa198}.highlight .sb{color:#93a1a1}.highlight .sc{color:#2aa198}.highlight .sd{color:#586e75}.highlight .s2{color:#2aa198}.highlight .se{color:#cb4b16}.highlight .sh{color:#586e75}.highlight .si,.highlight .sx{color:#2aa198}.highlight .sr{color:#dc322f}.highlight .s1,.highlight .ss{color:#2aa198}.highlight .bp,.highlight .vc,.highlight .vg,.highlight .vi{color:#268bd2}.highlight .il{color:#2aa198}.is-flex{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.is-clearfix:after{clear:both;content:" ";display:table}.is-pulled-left{float:left}.is-pulled-right{float:right}.is-overlay{bottom:0;left:0;position:absolute;right:0;top:0}.is-fullwidth{width:100%}.is-text-centered{text-align:center}.is-text-left{text-align:left}.is-text-right{text-align:right}@media screen and (max-width: 768px){.is-hidden-mobile{display:none !important}}@media screen and (min-width: 769px){.is-hidden-tablet{display:none !important}}@media screen and (max-width: 979px){.is-hidden-touch{display:none !important}}@media screen and (min-width: 980px){.is-hidden-desktop{display:none !important}}.is-disabled{pointer-events:none}.is-marginless{margin:0 !important}.is-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.input,.textarea{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;background:#fff;border:1px solid #d3d6db;border-radius:3px;color:#222324;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;font-size:14px;height:32px;line-height:24px;padding:3px 8px;position:relative;vertical-align:top;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);display:block;max-width:100%;width:100%}.input:hover,.textarea:hover{border-color:#aeb1b5}.input:active,.textarea:active,.input:focus,.textarea:focus{border-color:#1fc8db;outline:none}.input[disabled],[disabled].textarea,.input[disabled]:hover,[disabled].textarea:hover{background:#f5f7fa;border-color:#d3d6db;cursor:not-allowed}.input[disabled]::-moz-placeholder,[disabled].textarea::-moz-placeholder,.input[disabled]:hover::-moz-placeholder,[disabled].textarea:hover::-moz-placeholder{color:rgba(34,35,36,0.3)}.input[disabled]::-webkit-input-placeholder,[disabled].textarea::-webkit-input-placeholder,.input[disabled]:hover::-webkit-input-placeholder,[disabled].textarea:hover::-webkit-input-placeholder{color:rgba(34,35,36,0.3)}.input[disabled]:-moz-placeholder,[disabled].textarea:-moz-placeholder,.input[disabled]:hover:-moz-placeholder,[disabled].textarea:hover:-moz-placeholder{color:rgba(34,35,36,0.3)}.input[disabled]:-ms-input-placeholder,[disabled].textarea:-ms-input-placeholder,.input[disabled]:hover:-ms-input-placeholder,[disabled].textarea:hover:-ms-input-placeholder{color:rgba(34,35,36,0.3)}.input.is-dark,.is-dark.textarea{border-color:#222324}.input.is-primary,.is-primary.textarea{border-color:#1fc8db}.input.is-info,.is-info.textarea{border-color:#42afe3}.input.is-success,.is-success.textarea{border-color:#97cd76}.input.is-warning,.is-warning.textarea{border-color:#fce473}.input.is-danger,.is-danger.textarea{border-color:#ed6c63}.input[type="search"],[type="search"].textarea{border-radius:290486px}.input.is-flat,.is-flat.textarea{border:none;box-shadow:none;padding:4px 8px}.input.is-small,.is-small.textarea{border-radius:2px;font-size:11px;height:24px;line-height:16px;padding:3px 6px}.input.is-small.is-flat,.is-small.is-flat.textarea{padding:4px 6px}.input.is-medium,.is-medium.textarea{font-size:18px;height:40px;line-height:32px;padding:3px 10px}.input.is-medium.is-flat,.is-medium.is-flat.textarea{padding:4px 10px}.input.is-large,.is-large.textarea{font-size:24px;height:48px;line-height:40px;padding:3px 12px}.input.is-large.is-flat,.is-large.is-flat.textarea{padding:4px 12px}.input.is-fullwidth,.is-fullwidth.textarea{display:block;width:100%}.input.is-inline,.is-inline.textarea{display:inline;width:auto}.textarea{line-height:1.2;max-height:600px;max-width:100%;min-height:120px;min-width:100%;padding:10px;resize:vertical}.checkbox,.panel-checkbox,.radio{cursor:pointer;display:inline-block;line-height:16px;padding-left:18px;position:relative;vertical-align:top}.checkbox input,.panel-checkbox input,.radio input{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;background:#fff;border:1px solid #d3d6db;border-radius:3px;color:#222324;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;font-size:14px;height:32px;line-height:24px;padding:3px 8px;position:relative;vertical-align:top;border-radius:1px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.1);cursor:pointer;float:left;height:14px;left:0;outline:none;padding:0;position:absolute;top:1px;width:14px}.checkbox input:hover,.panel-checkbox input:hover,.radio input:hover{border-color:#aeb1b5}.checkbox input:active,.panel-checkbox input:active,.radio input:active,.checkbox input:focus,.panel-checkbox input:focus,.radio input:focus{border-color:#1fc8db;outline:none}.checkbox input[disabled],.panel-checkbox input[disabled],.radio input[disabled],.checkbox input[disabled]:hover,.panel-checkbox input[disabled]:hover,.radio input[disabled]:hover{background:#f5f7fa;border-color:#d3d6db;cursor:not-allowed}.checkbox input[disabled]::-moz-placeholder,.panel-checkbox input[disabled]::-moz-placeholder,.radio input[disabled]::-moz-placeholder,.checkbox input[disabled]:hover::-moz-placeholder,.panel-checkbox input[disabled]:hover::-moz-placeholder,.radio input[disabled]:hover::-moz-placeholder{color:rgba(34,35,36,0.3)}.checkbox input[disabled]::-webkit-input-placeholder,.panel-checkbox input[disabled]::-webkit-input-placeholder,.radio input[disabled]::-webkit-input-placeholder,.checkbox input[disabled]:hover::-webkit-input-placeholder,.panel-checkbox input[disabled]:hover::-webkit-input-placeholder,.radio input[disabled]:hover::-webkit-input-placeholder{color:rgba(34,35,36,0.3)}.checkbox input[disabled]:-moz-placeholder,.panel-checkbox input[disabled]:-moz-placeholder,.radio input[disabled]:-moz-placeholder,.checkbox input[disabled]:hover:-moz-placeholder,.panel-checkbox input[disabled]:hover:-moz-placeholder,.radio input[disabled]:hover:-moz-placeholder{color:rgba(34,35,36,0.3)}.checkbox input[disabled]:-ms-input-placeholder,.panel-checkbox input[disabled]:-ms-input-placeholder,.radio input[disabled]:-ms-input-placeholder,.checkbox input[disabled]:hover:-ms-input-placeholder,.panel-checkbox input[disabled]:hover:-ms-input-placeholder,.radio input[disabled]:hover:-ms-input-placeholder{color:rgba(34,35,36,0.3)}.checkbox input.is-dark,.panel-checkbox input.is-dark,.radio input.is-dark{border-color:#222324}.checkbox input.is-primary,.panel-checkbox input.is-primary,.radio input.is-primary{border-color:#1fc8db}.checkbox input.is-info,.panel-checkbox input.is-info,.radio input.is-info{border-color:#42afe3}.checkbox input.is-success,.panel-checkbox input.is-success,.radio input.is-success{border-color:#97cd76}.checkbox input.is-warning,.panel-checkbox input.is-warning,.radio input.is-warning{border-color:#fce473}.checkbox input.is-danger,.panel-checkbox input.is-danger,.radio input.is-danger{border-color:#ed6c63}.checkbox input:after,.panel-checkbox input:after,.radio input:after{border:1px solid #fff;border-right:0;border-top:0;content:" ";display:block;height:7px;pointer-events:none;position:absolute;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);width:7px;height:4px;left:3px;opacity:0;position:absolute;top:3px;-webkit-transform:rotate(-45deg) scale(1);transform:rotate(-45deg) scale(1)}.checkbox input:checked,.panel-checkbox input:checked,.radio input:checked{background:#1fc8db;border-color:#1fc8db;box-shadow:none}.checkbox input:checked:after,.panel-checkbox input:checked:after,.radio input:checked:after{opacity:1}.checkbox:hover,.panel-checkbox:hover,.radio:hover{color:#222324}.checkbox:hover input,.panel-checkbox:hover input,.radio:hover input{border-color:#aeb1b5}.checkbox:hover input:checked,.panel-checkbox:hover input:checked,.radio:hover input:checked{border-color:#1fc8db}.is-disabled.checkbox,.is-disabled.panel-checkbox,.is-disabled.radio,.is-disabled.checkbox:hover,.is-disabled.panel-checkbox:hover,.is-disabled.radio:hover{color:#aeb1b5}.radio+.radio{margin-left:10px}.radio input{border-radius:8px}.radio input:after{background:#fff;border:0;border-radius:2px;left:4px;top:4px;-webkit-transform:none;transform:none;width:4px}.select{display:inline-block;height:32px;position:relative;vertical-align:top}.select select{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;background:#fff;border:1px solid #d3d6db;border-radius:3px;color:#222324;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;font-size:14px;height:32px;line-height:24px;padding:3px 8px;position:relative;vertical-align:top;cursor:pointer;display:block;outline:none;padding-right:36px}.select select:hover{border-color:#aeb1b5}.select select:active,.select select:focus{border-color:#1fc8db;outline:none}.select select[disabled],.select select[disabled]:hover{background:#f5f7fa;border-color:#d3d6db;cursor:not-allowed}.select select[disabled]::-moz-placeholder,.select select[disabled]:hover::-moz-placeholder{color:rgba(34,35,36,0.3)}.select select[disabled]::-webkit-input-placeholder,.select select[disabled]:hover::-webkit-input-placeholder{color:rgba(34,35,36,0.3)}.select select[disabled]:-moz-placeholder,.select select[disabled]:hover:-moz-placeholder{color:rgba(34,35,36,0.3)}.select select[disabled]:-ms-input-placeholder,.select select[disabled]:hover:-ms-input-placeholder{color:rgba(34,35,36,0.3)}.select select.is-dark{border-color:#222324}.select select.is-primary{border-color:#1fc8db}.select select.is-info{border-color:#42afe3}.select select.is-success{border-color:#97cd76}.select select.is-warning{border-color:#fce473}.select select.is-danger{border-color:#ed6c63}.select select:hover{border-color:#aeb1b5}.select select::ms-expand{display:none}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select:after{border:1px solid #1fc8db;border-right:0;border-top:0;content:" ";display:block;height:7px;pointer-events:none;position:absolute;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);width:7px;margin-top:-6px;right:16px;top:50%}.select:hover:after{border-color:#222324}.label{color:#222324;display:block;font-weight:bold}.label:not(:last-child){margin-bottom:5px}.help{display:block;font-size:11px;margin-top:5px}.help.is-dark{color:#222324}.help.is-primary{color:#1fc8db}.help.is-info{color:#42afe3}.help.is-success{color:#97cd76}.help.is-warning{color:#fce473}.help.is-danger{color:#ed6c63}@media screen and (max-width: 768px){.control-label{margin-bottom:5px}}@media screen and (min-width: 769px){.control-label{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;margin-right:20px;padding-top:7px;text-align:right}}.control{position:relative;text-align:left}.control.is-loading:after{position:absolute !important;right:8px;top:8px}.control:not(:last-child){margin-bottom:10px}.control.has-icon>.fa{display:inline-block;font-size:14px;height:16px;line-height:16px;text-align:center;vertical-align:top;width:16px;color:#aeb1b5;pointer-events:none;position:absolute;top:8px;z-index:4}.control.has-icon .input:focus+.fa,.control.has-icon .textarea:focus+.fa{color:#222324}.control.has-icon:not(.has-icon-right)>.fa{left:8px}.control.has-icon:not(.has-icon-right) .input,.control.has-icon:not(.has-icon-right) .textarea{padding-left:32px}.control.has-icon-right>.fa{right:8px}.control.has-icon-right .input,.control.has-icon-right .textarea{padding-right:32px}.control.has-addons{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.control.has-addons .input,.control.has-addons .textarea,.control.has-addons .button,.control.has-addons .pagination a,.pagination .control.has-addons a,.control.has-addons .select{border-radius:0;margin-right:-1px}.control.has-addons .input:hover,.control.has-addons .textarea:hover,.control.has-addons .button:hover,.control.has-addons .pagination a:hover,.pagination .control.has-addons a:hover,.control.has-addons .select:hover{z-index:2}.control.has-addons .input:active,.control.has-addons .textarea:active,.control.has-addons .input:focus,.control.has-addons .textarea:focus,.control.has-addons .button:active,.control.has-addons .pagination a:active,.pagination .control.has-addons a:active,.control.has-addons .button:focus,.control.has-addons .pagination a:focus,.pagination .control.has-addons a:focus,.control.has-addons .select:active,.control.has-addons .select:focus{z-index:3}.control.has-addons .input:first-child,.control.has-addons .textarea:first-child,.control.has-addons .button:first-child,.control.has-addons .pagination a:first-child,.pagination .control.has-addons a:first-child,.control.has-addons .select:first-child{border-radius:3px 0 0 3px}.control.has-addons .input:first-child select,.control.has-addons .textarea:first-child select,.control.has-addons .button:first-child select,.control.has-addons .pagination a:first-child select,.pagination .control.has-addons a:first-child select,.control.has-addons .select:first-child select{border-radius:3px 0 0 3px}.control.has-addons .input:last-child,.control.has-addons .textarea:last-child,.control.has-addons .button:last-child,.control.has-addons .pagination a:last-child,.pagination .control.has-addons a:last-child,.control.has-addons .select:last-child{border-radius:0 3px 3px 0}.control.has-addons.is-centered{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.control.is-grouped{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.control.is-grouped>.button:not(:last-child),.pagination .control.is-grouped>a:not(:last-child),.control.is-grouped>.input:not(:last-child),.control.is-grouped>.textarea:not(:last-child),.control.is-grouped>.select:not(:last-child){margin-right:10px}.control.is-grouped>.input,.control.is-grouped>.textarea{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}@media screen and (min-width: 769px){.control.is-horizontal{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.control.is-horizontal>.control{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:5;-webkit-flex:5;-ms-flex:5;flex:5}}.button,.pagination a{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;background:#fff;border:1px solid #d3d6db;border-radius:3px;color:#222324;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;font-size:14px;height:32px;line-height:24px;padding:3px 8px;position:relative;vertical-align:top;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;padding:3px 10px;text-align:center;white-space:nowrap}.button:hover,.pagination a:hover{border-color:#aeb1b5}.button:active,.pagination a:active,.button:focus,.pagination a:focus{border-color:#1fc8db;outline:none}.button[disabled],.pagination a[disabled],.button[disabled]:hover,.pagination a[disabled]:hover{background:#f5f7fa;border-color:#d3d6db;cursor:not-allowed}.button[disabled]::-moz-placeholder,.pagination a[disabled]::-moz-placeholder,.button[disabled]:hover::-moz-placeholder,.pagination a[disabled]:hover::-moz-placeholder{color:rgba(34,35,36,0.3)}.button[disabled]::-webkit-input-placeholder,.pagination a[disabled]::-webkit-input-placeholder,.button[disabled]:hover::-webkit-input-placeholder,.pagination a[disabled]:hover::-webkit-input-placeholder{color:rgba(34,35,36,0.3)}.button[disabled]:-moz-placeholder,.pagination a[disabled]:-moz-placeholder,.button[disabled]:hover:-moz-placeholder,.pagination a[disabled]:hover:-moz-placeholder{color:rgba(34,35,36,0.3)}.button[disabled]:-ms-input-placeholder,.pagination a[disabled]:-ms-input-placeholder,.button[disabled]:hover:-ms-input-placeholder,.pagination a[disabled]:hover:-ms-input-placeholder{color:rgba(34,35,36,0.3)}.button strong,.pagination a strong{color:inherit}.button small,.pagination a small{display:block;font-size:11px;line-height:1;margin-top:5px}.button .icon:first-child,.pagination a .icon:first-child{margin-right:4px}.button .icon:last-child,.pagination a .icon:last-child{margin-left:4px}.button:hover,.pagination a:hover{color:#222324}.button:active,.pagination a:active{box-shadow:inset 0 1px 2px rgba(0,0,0,0.2)}.button.is-dark,.pagination a.is-dark{background:#222324;border-color:transparent;color:#fff}.button.is-dark:hover,.pagination a.is-dark:hover,.button.is-dark:focus,.pagination a.is-dark:focus{background:#090a0a;border-color:transparent;color:#fff}.button.is-dark:active,.pagination a.is-dark:active{border-color:transparent}.button.is-dark.is-outlined,.pagination a.is-dark.is-outlined{background:transparent;border-color:#222324;color:#222324}.button.is-dark.is-outlined:hover,.pagination a.is-dark.is-outlined:hover,.button.is-dark.is-outlined:focus,.pagination a.is-dark.is-outlined:focus{border-color:#090a0a;color:#090a0a}.button.is-dark.is-inverted,.pagination a.is-dark.is-inverted{background:#fff;color:#222324}.button.is-dark.is-inverted:hover,.pagination a.is-dark.is-inverted:hover{background:#f2f2f2}.button.is-dark.is-inverted.is-outlined,.pagination a.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-dark.is-inverted.is-outlined:hover,.pagination a.is-dark.is-inverted.is-outlined:hover{background:rgba(0,0,0,0.05)}.button.is-dark.is-loading:after,.pagination a.is-dark.is-loading:after{border-color:transparent transparent #fff #fff !important}.button.is-primary,.pagination a.is-primary{background:#1fc8db;border-color:transparent;color:#fff}.button.is-primary:hover,.pagination a.is-primary:hover,.button.is-primary:focus,.pagination a.is-primary:focus{background:#199fae;border-color:transparent;color:#fff}.button.is-primary:active,.pagination a.is-primary:active{border-color:transparent}.button.is-primary.is-outlined,.pagination a.is-primary.is-outlined{background:transparent;border-color:#1fc8db;color:#1fc8db}.button.is-primary.is-outlined:hover,.pagination a.is-primary.is-outlined:hover,.button.is-primary.is-outlined:focus,.pagination a.is-primary.is-outlined:focus{border-color:#199fae;color:#199fae}.button.is-primary.is-inverted,.pagination a.is-primary.is-inverted{background:#fff;color:#1fc8db}.button.is-primary.is-inverted:hover,.pagination a.is-primary.is-inverted:hover{background:#f2f2f2}.button.is-primary.is-inverted.is-outlined,.pagination a.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:hover,.pagination a.is-primary.is-inverted.is-outlined:hover{background:rgba(0,0,0,0.05)}.button.is-primary.is-loading:after,.pagination a.is-primary.is-loading:after{border-color:transparent transparent #fff #fff !important}.button.is-info,.pagination a.is-info{background:#42afe3;border-color:transparent;color:#fff}.button.is-info:hover,.pagination a.is-info:hover,.button.is-info:focus,.pagination a.is-info:focus{background:#1f99d3;border-color:transparent;color:#fff}.button.is-info:active,.pagination a.is-info:active{border-color:transparent}.button.is-info.is-outlined,.pagination a.is-info.is-outlined{background:transparent;border-color:#42afe3;color:#42afe3}.button.is-info.is-outlined:hover,.pagination a.is-info.is-outlined:hover,.button.is-info.is-outlined:focus,.pagination a.is-info.is-outlined:focus{border-color:#1f99d3;color:#1f99d3}.button.is-info.is-inverted,.pagination a.is-info.is-inverted{background:#fff;color:#42afe3}.button.is-info.is-inverted:hover,.pagination a.is-info.is-inverted:hover{background:#f2f2f2}.button.is-info.is-inverted.is-outlined,.pagination a.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:hover,.pagination a.is-info.is-inverted.is-outlined:hover{background:rgba(0,0,0,0.05)}.button.is-info.is-loading:after,.pagination a.is-info.is-loading:after{border-color:transparent transparent #fff #fff !important}.button.is-success,.pagination a.is-success{background:#97cd76;border-color:transparent;color:#fff}.button.is-success:hover,.pagination a.is-success:hover,.button.is-success:focus,.pagination a.is-success:focus{background:#7bbf51;border-color:transparent;color:#fff}.button.is-success:active,.pagination a.is-success:active{border-color:transparent}.button.is-success.is-outlined,.pagination a.is-success.is-outlined{background:transparent;border-color:#97cd76;color:#97cd76}.button.is-success.is-outlined:hover,.pagination a.is-success.is-outlined:hover,.button.is-success.is-outlined:focus,.pagination a.is-success.is-outlined:focus{border-color:#7bbf51;color:#7bbf51}.button.is-success.is-inverted,.pagination a.is-success.is-inverted{background:#fff;color:#97cd76}.button.is-success.is-inverted:hover,.pagination a.is-success.is-inverted:hover{background:#f2f2f2}.button.is-success.is-inverted.is-outlined,.pagination a.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:hover,.pagination a.is-success.is-inverted.is-outlined:hover{background:rgba(0,0,0,0.05)}.button.is-success.is-loading:after,.pagination a.is-success.is-loading:after{border-color:transparent transparent #fff #fff !important}.button.is-warning,.pagination a.is-warning{background:#fce473;border-color:transparent;color:rgba(0,0,0,0.5)}.button.is-warning:hover,.pagination a.is-warning:hover,.button.is-warning:focus,.pagination a.is-warning:focus{background:#fbda41;border-color:transparent;color:rgba(0,0,0,0.5)}.button.is-warning:active,.pagination a.is-warning:active{border-color:transparent}.button.is-warning.is-outlined,.pagination a.is-warning.is-outlined{background:transparent;border-color:#fce473;color:#fce473}.button.is-warning.is-outlined:hover,.pagination a.is-warning.is-outlined:hover,.button.is-warning.is-outlined:focus,.pagination a.is-warning.is-outlined:focus{border-color:#fbda41;color:#fbda41}.button.is-warning.is-inverted,.pagination a.is-warning.is-inverted{background:rgba(0,0,0,0.5);color:#fce473}.button.is-warning.is-inverted:hover,.pagination a.is-warning.is-inverted:hover{background:rgba(0,0,0,0.5)}.button.is-warning.is-inverted.is-outlined,.pagination a.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.5);color:rgba(0,0,0,0.5)}.button.is-warning.is-inverted.is-outlined:hover,.pagination a.is-warning.is-inverted.is-outlined:hover{background:rgba(0,0,0,0.05)}.button.is-warning.is-loading:after,.pagination a.is-warning.is-loading:after{border-color:transparent transparent rgba(0,0,0,0.5) rgba(0,0,0,0.5) !important}.button.is-danger,.pagination a.is-danger{background:#ed6c63;border-color:transparent;color:#fff}.button.is-danger:hover,.pagination a.is-danger:hover,.button.is-danger:focus,.pagination a.is-danger:focus{background:#e84135;border-color:transparent;color:#fff}.button.is-danger:active,.pagination a.is-danger:active{border-color:transparent}.button.is-danger.is-outlined,.pagination a.is-danger.is-outlined{background:transparent;border-color:#ed6c63;color:#ed6c63}.button.is-danger.is-outlined:hover,.pagination a.is-danger.is-outlined:hover,.button.is-danger.is-outlined:focus,.pagination a.is-danger.is-outlined:focus{border-color:#e84135;color:#e84135}.button.is-danger.is-inverted,.pagination a.is-danger.is-inverted{background:#fff;color:#ed6c63}.button.is-danger.is-inverted:hover,.pagination a.is-danger.is-inverted:hover{background:#f2f2f2}.button.is-danger.is-inverted.is-outlined,.pagination a.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:hover,.pagination a.is-danger.is-inverted.is-outlined:hover{background:rgba(0,0,0,0.05)}.button.is-danger.is-loading:after,.pagination a.is-danger.is-loading:after{border-color:transparent transparent #fff #fff !important}.button.is-link,.pagination a.is-link{border-color:transparent;color:#69707a;text-decoration:underline}.button.is-link:hover,.pagination a.is-link:hover,.button.is-link:focus,.pagination a.is-link:focus{background:#d3d6db;color:#222324}.button.is-small,.pagination a.is-small{border-radius:2px;font-size:11px;height:24px;line-height:16px;padding:3px 6px}.button.is-medium,.pagination a.is-medium{font-size:18px;height:40px;padding:7px 14px}.button.is-large,.pagination a.is-large{font-size:22px;height:48px;padding:11px 20px}.button.is-fullwidth,.pagination a.is-fullwidth{display:block;width:100%}.button.is-flexible,.pagination a.is-flexible{height:auto}.button.is-loading,.pagination a.is-loading{color:transparent;pointer-events:none}.button.is-loading:after,.pagination a.is-loading:after{left:50%;margin-left:-8px;margin-top:-8px;position:absolute;top:50%;position:absolute !important}.button.is-disabled,.pagination a.is-disabled,.button[disabled],.pagination a[disabled]{opacity:0.5;pointer-events:none}.title,.subtitle{font-weight:300}.title em,.title span,.subtitle em,.subtitle span{font-weight:300}.title strong,.subtitle strong{font-weight:500}.title a:hover,.subtitle a:hover{border-bottom:1px solid}.title .tag,.subtitle .tag{vertical-align:bottom}.title{color:#222324;font-size:28px;line-height:1}.title strong{color:inherit}.title code{display:inline-block;font-size:28px}.title+.subtitle{margin-top:-10px}.title+.highlight{margin-top:-10px}.title.is-normal{font-weight:400}.title.is-normal strong{font-weight:700}.title.is-1{font-size:48px}.title.is-1 code{font-size:40px}.title.is-2{font-size:40px}.title.is-2 code{font-size:28px}.title.is-3{font-size:28px}.title.is-3 code{font-size:24px}.title.is-4{font-size:24px}.title.is-4 code{font-size:18px}.title.is-5{font-size:18px}.title.is-5 code{font-size:14px}.title.is-6{font-size:14px}.title.is-6 code{font-size:14px}@media screen and (min-width: 769px){.title+.subtitle{margin-top:-15px}}.subtitle{font-size:18px;line-height:1.125}.subtitle+.title{margin-top:-20px}.subtitle strong{color:#222324}.subtitle code{border-radius:3px;display:inline-block;font-size:14px;padding:2px 3px;vertical-align:top}.subtitle+.text{margin-top:20px}.subtitle.is-normal{font-weight:400}.subtitle.is-normal strong{font-weight:700}.subtitle.is-1{font-size:48px}.subtitle.is-1 code{font-size:40px}.subtitle.is-2{font-size:40px}.subtitle.is-2 code{font-size:28px}.subtitle.is-3{font-size:28px}.subtitle.is-3 code{font-size:24px}.subtitle.is-4{font-size:24px}.subtitle.is-4 code{font-size:18px}.subtitle.is-5{font-size:18px}.subtitle.is-5 code{font-size:14px}.subtitle.is-6{font-size:14px}.subtitle.is-6 code{font-size:14px}.image{display:block;position:relative}.image img{display:block}.image.is-square img,.image.is-1by1 img,.image.is-4by3 img,.image.is-3by2 img,.image.is-16by9 img,.image.is-2by1 img{bottom:0;left:0;position:absolute;right:0;top:0;height:100%;width:100%}.image.is-square,.image.is-1by1{padding-top:100%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.message-body{border:1px solid #d3d6db;border-radius:3px;padding:12px 15px}.message-body strong{color:inherit}.message-header{background:#69707a;border-radius:3px 3px 0 0;color:#fff;font-size:10px;font-weight:bold;letter-spacing:1px;padding:3px 8px;text-transform:uppercase}.message-header+.message-body{border-radius:0 0 3px 3px;border-top:none}.message{background:#f5f7fa;border-radius:3px}.message.is-dark{background:#f5f5f5}.message.is-dark .message-header{background:#222324;color:#fff}.message.is-dark .message-body{border-color:#222324;color:gray}.message.is-primary{background:#edfbfc}.message.is-primary .message-header{background:#1fc8db;color:#fff}.message.is-primary .message-body{border-color:#1fc8db;color:gray}.message.is-info{background:#edf7fc}.message.is-info .message-header{background:#42afe3;color:#fff}.message.is-info .message-body{border-color:#42afe3;color:gray}.message.is-success{background:#f4faf0}.message.is-success .message-header{background:#97cd76;color:#fff}.message.is-success .message-body{border-color:#97cd76;color:gray}.message.is-warning{background:#fffbeb}.message.is-warning .message-header{background:#fce473;color:rgba(0,0,0,0.5)}.message.is-warning .message-body{border-color:#fce473;color:#666}.message.is-danger{background:#fdeeed}.message.is-danger .message-header{background:#ed6c63;color:#fff}.message.is-danger .message-body{border-color:#ed6c63;color:gray}.notification{background:#f5f7fa;border-radius:3px;padding:16px 20px;position:relative}.notification:after{clear:both;content:" ";display:table}.notification .title{color:inherit}.notification.is-dark{background:#222324;color:#fff}.notification.is-primary{background:#1fc8db;color:#fff}.notification.is-info{background:#42afe3;color:#fff}.notification.is-success{background:#97cd76;color:#fff}.notification.is-warning{background:#fce473;color:rgba(0,0,0,0.5)}.notification.is-danger{background:#ed6c63;color:#fff}.notification .delete,.notification .modal-close{background:rgba(0,0,0,0.2);border-radius:0 3px;float:right;margin:-16px -20px 0 20px}.notification .delete:hover,.notification .modal-close:hover{background:rgba(0,0,0,0.5)}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:12px;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background:#d3d6db}.progress::-webkit-progress-value{background:#69707a}.progress::-moz-progress-bar{background:#69707a}.progress.is-small{height:8px}.progress.is-medium{height:16px}.progress.is-large{height:20px}.progress.is-dark::-webkit-progress-value{background:#222324}.progress.is-dark::-moz-progress-bar{background:#222324}.progress.is-primary::-webkit-progress-value{background:#1fc8db}.progress.is-primary::-moz-progress-bar{background:#1fc8db}.progress.is-info::-webkit-progress-value{background:#42afe3}.progress.is-info::-moz-progress-bar{background:#42afe3}.progress.is-success::-webkit-progress-value{background:#97cd76}.progress.is-success::-moz-progress-bar{background:#97cd76}.progress.is-warning::-webkit-progress-value{background:#fce473}.progress.is-warning::-moz-progress-bar{background:#fce473}.progress.is-danger::-webkit-progress-value{background:#ed6c63}.progress.is-danger::-moz-progress-bar{background:#ed6c63}.delete,.modal-close{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-moz-appearance:none;-webkit-appearance:none;background:rgba(0,0,0,0.2);border:none;border-radius:290486px;cursor:pointer;display:inline-block;height:24px;position:relative;vertical-align:top;width:24px}.delete:before,.modal-close:before,.delete:after,.modal-close:after{background:white;content:"";display:block;height:2px;left:50%;margin-left:-25%;margin-top:-1px;position:absolute;top:50%;width:50%}.delete:before,.modal-close:before{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.delete:after,.modal-close:after{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.delete:hover,.modal-close:hover{background:#ed6c63}.delete.is-small,.tag:not(.is-large) .delete,.tag:not(.is-large) .modal-close,.is-small.modal-close{height:16px;width:16px}.delete.is-medium,.is-medium.modal-close{height:32px;width:32px}.delete.is-large,.is-large.modal-close{height:40px;width:40px}.icon{display:inline-block;font-size:21px;height:24px;line-height:24px;text-align:center;vertical-align:top;width:24px}.icon .fa{font-size:inherit;line-height:inherit}.icon.is-small{display:inline-block;font-size:14px;height:16px;line-height:16px;text-align:center;vertical-align:top;width:16px}.icon.is-medium{display:inline-block;font-size:28px;height:32px;line-height:32px;text-align:center;vertical-align:top;width:32px}.icon.is-large{display:inline-block;font-size:42px;height:48px;line-height:48px;text-align:center;vertical-align:top;width:48px}.hamburger,.header-toggle{cursor:pointer;display:block;height:50px;position:relative;width:50px}.hamburger span,.header-toggle span{background:#69707a;display:block;height:1px;left:50%;margin-left:-7px;position:absolute;top:50%;-webkit-transition:none 86ms ease-out;transition:none 86ms ease-out;-webkit-transition-property:background, left, opacity, -webkit-transform;transition-property:background, left, opacity, -webkit-transform;transition-property:background, left, opacity, transform;transition-property:background, left, opacity, transform, -webkit-transform;width:15px}.hamburger span:nth-child(1),.header-toggle span:nth-child(1){margin-top:-6px}.hamburger span:nth-child(2),.header-toggle span:nth-child(2){margin-top:-1px}.hamburger span:nth-child(3),.header-toggle span:nth-child(3){margin-top:4px}.hamburger:hover,.header-toggle:hover{background:#f5f7fa}.hamburger.is-active span,.is-active.header-toggle span{background:#1fc8db}.hamburger.is-active span:nth-child(1),.is-active.header-toggle span:nth-child(1){margin-left:-5px;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-transform-origin:left top;transform-origin:left top}.hamburger.is-active span:nth-child(2),.is-active.header-toggle span:nth-child(2){opacity:0}.hamburger.is-active span:nth-child(3),.is-active.header-toggle span:nth-child(3){margin-left:-5px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}@media screen and (min-width: 769px){.hamburger,.header-toggle{height:50px;width:50px}}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-size:12px;font-weight:normal;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.image{display:block;position:relative;vertical-align:top}.image img{bottom:0;left:0;position:absolute;right:0;top:0;display:block;width:100%}.image.is-3x2{padding-top:66.6666%}.loader,.control.is-loading:after,.button.is-loading:after,.pagination a.is-loading:after{-webkit-animation:spin-around 500ms infinite linear;animation:spin-around 500ms infinite linear;border:2px solid #d3d6db;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:16px;position:relative;width:16px}.number{background:#f5f7fa;border-radius:290486px;display:inline-block;font-size:18px;vertical-align:top}.tag{background:#f5f7fa;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.1);color:#69707a;display:inline-block;font-size:12px;height:24px;line-height:16px;padding:4px 10px;vertical-align:top;white-space:nowrap}.tag.is-dark{background:#69707a;color:#fff}.tag.is-rounded{border-radius:290486px}.tag.is-medium{box-shadow:inset 0 -2px 0 rgba(0,0,0,0.1);font-size:14px;height:32px;padding:7px 14px 9px}.tag:not(.is-large) .delete,.tag:not(.is-large) .modal-close{margin-left:4px;margin-right:-6px}.tag.is-large{box-shadow:inset 0 -2px 0 rgba(0,0,0,0.1);font-size:18px;height:40px;line-height:24px;padding:7px 18px 9px}.tag.is-large .delete,.tag.is-large .modal-close{margin-left:4px;margin-right:-8px}.tag.is-dark{background:#222324;color:#fff}.tag.is-primary{background:#1fc8db;color:#fff}.tag.is-info{background:#42afe3;color:#fff}.tag.is-success{background:#97cd76;color:#fff}.tag.is-warning{background:#fce473;color:rgba(0,0,0,0.5)}.tag.is-danger{background:#ed6c63;color:#fff}.column{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;padding:10px}.columns.is-mobile>.column.is-half{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.columns.is-mobile>.column.is-third{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.3333%}.columns.is-mobile>.column.is-quarter{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-quarter{margin-left:25%}.columns.is-mobile>.column.is-1{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}@media screen and (max-width: 768px){.column.is-half-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.column.is-third-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.3333%}.column.is-quarter-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-third-mobile{margin-left:33.3333%}.column.is-offset-quarter-mobile{margin-left:25%}.column.is-1-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}}@media screen and (min-width: 769px){.column.is-half,.column.is-half-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.column.is-third,.column.is-third-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.3333%}.column.is-quarter,.column.is-quarter-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-third,.column.is-offset-third-tablet{margin-left:33.3333%}.column.is-offset-quarter,.column.is-offset-quarter-tablet{margin-left:25%}.column.is-1,.column.is-1-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}}@media screen and (min-width: 980px){.column.is-half-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.column.is-third-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.3333%}.column.is-quarter-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-third-desktop{margin-left:33.3333%}.column.is-offset-quarter-desktop{margin-left:25%}.column.is-1-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}}.columns{margin-left:-10px;margin-right:-10px;margin-top:-10px}.columns:last-child{margin-bottom:-10px}.columns:not(:last-child){margin-bottom:10px}.columns.is-centered{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.columns.is-mobile{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.columns.is-gapless{margin-left:0;margin-right:0}.columns.is-gapless:not(:last-child){margin-bottom:20px}.columns.is-gapless>.column{margin:0;padding:0}.columns.is-multiline{-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.columns.is-vcentered{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center}@media screen and (min-width: 769px){.columns.is-grid{-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.columns.is-grid>.column{-webkit-flex-basis:33.3333%;-ms-flex-preferred-size:33.3333%;flex-basis:33.3333%;max-width:33.3333%;padding:10px;width:33.3333%}.columns.is-grid>.column+.column{margin-left:0}}@media screen and (min-width: 769px){.columns:not(.is-desktop){display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}}@media screen and (min-width: 980px){.columns.is-desktop{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}}.navbar-item .title,.navbar-item .subtitle{margin-bottom:0}@media screen and (max-width: 768px){.navbar-item:not(:last-child){margin-bottom:10px}}.navbar code{border-radius:3px}.navbar img{display:inline-block;vertical-align:top}@media screen and (min-width: 769px){.navbar{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.navbar>.navbar-item:not(.is-narrow){-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}}.navbar-left .navbar-item.is-flexible,.navbar-right .navbar-item.is-flexible{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.navbar-left .navbar-item:not(:last-child),.navbar-right .navbar-item:not(:last-child){margin-right:10px}@media screen and (max-width: 768px){.navbar-left+.navbar-right{margin-top:20px}}@media screen and (min-width: 769px){.navbar-left{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}}@media screen and (min-width: 769px){.navbar-right{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}}.card-header{-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;box-shadow:0 1px 2px rgba(0,0,0,0.1);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:40px}.card-header-title{-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;color:#222324;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:bold;padding:10px}.card-header-icon{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;cursor:pointer;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;width:40px}.card-image{display:block;position:relative}.card-content{padding:20px}.card-content .title+.subtitle{margin-top:-20px}.card-footer{background:#f5f7fa;border-top:1px solid #d3d6db;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.card-footer-item{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;padding:10px}.card-footer-item:not(:last-child){border-right:1px solid #d3d6db}.card{background:white;box-shadow:0 2px 3px rgba(0,0,0,0.1),0 0 0 1px rgba(0,0,0,0.1);max-width:100%;position:relative;width:300px}.card .media:not(:last-child){margin-bottom:10px}.card.is-rounded{border-radius:5px}.card.is-fullwidth{width:100%}.table{background:white;color:#222324;margin-bottom:20px;width:100%}.table th,.table td{border:1px solid #d3d6db;border-width:0 0 1px;padding:8px 10px;vertical-align:top}.table th.table-narrow,.table td.table-narrow{white-space:nowrap;width:1%}.table th.table-link,.table td.table-link{padding:0}.table th.table-link>a,.table td.table-link>a{display:block;padding:8px 10px}.table th.table-link>a:hover,.table td.table-link>a:hover{background:#1fc8db;color:#fff}.table th.table-icon,.table td.table-icon{padding:5px;text-align:center;white-space:nowrap;width:1%}.table th.table-icon .fa,.table td.table-icon .fa{display:inline-block;font-size:21px;height:24px;line-height:24px;text-align:center;vertical-align:top;width:24px}.table th.table-icon.table-link,.table td.table-icon.table-link{padding:0}.table th.table-icon.table-link>a,.table td.table-icon.table-link>a{padding:5px}.table th{color:#222324;text-align:left}.table tr:hover{background:rgba(245,247,250,0.5);color:#222324}.table tr:last-child td{border-bottom-width:0}.table thead th,.table thead td{border-width:0 0 2px;color:#aeb1b5}.table tfoot th,.table tfoot td{border-width:2px 0 0;color:#aeb1b5}.table.is-bordered th,.table.is-bordered td{border-width:1px}.table.is-bordered tr:last-child td{border-bottom-width:1px}.table.is-narrow th,.table.is-narrow td{padding:5px 10px}.table.is-narrow th.table-link,.table.is-narrow td.table-link{padding:0}.table.is-narrow th.table-link>a,.table.is-narrow td.table-link>a{padding:5px 10px}.table.is-narrow th.table-icon,.table.is-narrow td.table-icon{padding:2px}.table.is-narrow th.table-icon.table-link,.table.is-narrow td.table-icon.table-link{padding:0}.table.is-narrow th.table-icon.table-link>a,.table.is-narrow td.table-icon.table-link>a{padding:2px}.table.is-striped tbody tr:nth-child(2n){background:rgba(245,247,250,0.5)}.table.is-striped tbody tr:nth-child(2n):hover{background:#f5f7fa}.tabs{line-height:24px;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs .fa{font-size:14px;line-height:20px;margin:2px -2px;width:20px}.tabs a{border-bottom:1px solid #d3d6db;color:#69707a;display:block;margin-bottom:-1px;padding:5px 0;vertical-align:top}.tabs a:hover{border-bottom-color:#222324;color:#222324}.tabs li{display:block;vertical-align:top}.tabs li+li{margin-left:20px}.tabs li.is-active a{border-bottom-color:#1fc8db;color:#1fc8db}.tabs ul{border-bottom:1px solid #d3d6db;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.tabs.is-centered a{padding:5px 10px}.tabs.is-centered li+li{margin-left:0}.tabs.is-centered ul{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center}.tabs.is-right ul{-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:3px 3px 0 0;padding:5px 15px}.tabs.is-boxed a:hover{background:#f5f7fa;border-bottom-color:#d3d6db}.tabs.is-boxed li+li{margin-left:5px}.tabs.is-boxed li.is-active a{background:white;border-color:#d3d6db;border-bottom-color:transparent}.tabs.is-boxed.is-centered li,.tabs.is-boxed.is-centered li+li{margin:0 2px}.tabs.is-toggle a{border:1px solid #d3d6db;margin-bottom:0;padding:5px 10px;position:relative}.tabs.is-toggle a:hover{background:#f5f7fa;border-color:#aeb1b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:3px 0 0 3px}.tabs.is-toggle li:last-child a{border-radius:0 3px 3px 0}.tabs.is-toggle li.is-active a{background:#1fc8db;border-color:#1fc8db;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}@media screen and (min-width: 769px){.tabs.is-fullwidth li{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.tabs.is-fullwidth li+li{margin-left:0}.tabs.is-fullwidth ul{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center}}.media-number{background:#f5f7fa;border-radius:290486px;display:inline-block;font-size:18px;height:32px;line-height:24px;min-width:32px;padding:4px 8px;text-align:center;vertical-align:top}@media screen and (max-width: 768px){.media-number{margin-bottom:10px}}@media screen and (min-width: 769px){.media-number{margin-right:10px}}.media-left{margin-right:10px}.media-right{margin-left:10px}.media-content{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;text-align:left}.media{-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:10px}.media .media{border-top:1px solid rgba(211,214,219,0.5);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding-top:10px}.media .media .textarea{border-radius:2px;font-size:11px;height:24px;line-height:16px;padding:3px 6px}.media .media .button,.media .media .pagination a,.pagination .media .media a{border-radius:2px;font-size:11px;height:24px;line-height:16px;padding:3px 6px}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:5px}.media .media .media{font-size:12px;padding-top:5px}.media .media .media+.media{margin-top:5px}.media+.media{border-top:1px solid rgba(211,214,219,0.5);margin-top:10px;padding-top:10px}.media.is-large+.media{margin-top:20px;padding-top:20px}@media screen and (min-width: 769px){.media.is-large .media-number{margin-right:20px}}.menu-nav a{display:block;padding:5px 10px}.menu-list a{border-radius:2px;color:#69707a;display:block;padding:5px 10px}.menu-list a:hover{background:#f5f7fa;color:#1fc8db}.menu-list a.is-active{background:#1fc8db;color:#fff}.menu-list li ul{border-left:1px solid #d3d6db;margin:10px;padding-left:10px}.menu-label{color:#aeb1b5;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.menu-label:not(:first-child){margin-top:20px}.pagination{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center}.pagination a{display:block;min-width:32px;padding:3px 8px}.pagination a.is-active{background:#1fc8db;border-color:#1fc8db;color:#fff}.pagination span{color:#aeb1b5;display:block;margin:0 4px}.pagination li{margin:0 2px}.pagination ul{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}@media screen and (max-width: 768px){.pagination{-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.pagination>a{width:calc(50% - 5px)}.pagination>a:not(:first-child){margin-left:10px}.pagination li{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.pagination ul{margin-top:10px}}@media screen and (min-width: 769px){.pagination>a:not(:first-child){-webkit-box-ordinal-group:2;-webkit-order:1;-ms-flex-order:1;order:1}}.panel-icon{display:inline-block;font-size:14px;height:16px;line-height:16px;text-align:center;vertical-align:top;width:16px;color:#aeb1b5;float:left;margin:0 4px 0 -2px}.panel-icon .fa{font-size:inherit;line-height:inherit}.panel-heading{background:#f5f7fa;border-bottom:1px solid #d3d6db;border-radius:4px 4px 0 0;color:#222324;font-size:18px;font-weight:300;padding:10px}.panel-list a{color:#69707a}.panel-list a:hover{color:#1fc8db}.panel-tabs{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:11px;padding:5px 10px 0;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.panel-tabs:not(:last-child){border-bottom:1px solid #d3d6db}.panel-tabs a{border-bottom:1px solid #d3d6db;margin-bottom:-1px;padding:5px}.panel-tabs a.is-active{border-bottom-color:#222324;color:#222324}.panel-block{color:#222324;display:block;line-height:16px;padding:10px}.panel-block:not(:last-child){border-bottom:1px solid #d3d6db}.panel-block .checkbox,.panel-block .panel-checkbox{border:1px solid transparent;border-radius:3px;display:block;padding:8px;padding-left:32px}.panel-block .checkbox input,.panel-block .panel-checkbox input{left:9px;top:9px}.panel-block .checkbox:hover,.panel-block .panel-checkbox:hover{border-color:#1fc8db}a.panel-block:hover{background:#f5f7fa}.panel-checkbox{display:block;padding:9px 10px 9px 30px}.panel-checkbox:not(:last-child){border-bottom:1px solid #d3d6db}.panel-checkbox input{left:8px;top:10px}.panel{border:1px solid #d3d6db;border-radius:5px}.panel:not(:last-child){margin-bottom:20px}.modal-background{bottom:0;left:0;position:absolute;right:0;top:0;background:rgba(0,0,0,0.86)}.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width: 769px){.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:none;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal{bottom:0;left:0;position:absolute;right:0;top:0;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;display:none;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;overflow:hidden;position:fixed;z-index:1986}.modal.is-active{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.box{background:white;border-radius:5px;box-shadow:0 2px 3px rgba(0,0,0,0.1),0 0 0 1px rgba(0,0,0,0.1);padding:20px}.header{background:white;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;line-height:24px;position:relative;text-align:center;z-index:2}.header:after{clear:both;content:" ";display:table}.header .container{-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%}.header.has-shadow{box-shadow:0 1px 2px rgba(0,0,0,0.1)}@media screen and (max-width: 768px){.header .container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}}@media screen and (min-width: 769px){.header{height:50px}}.header-toggle{position:absolute;right:0;top:0}@media screen and (min-width: 769px){.header-toggle{display:none}}.header-item{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px}.header-item img{max-height:24px}.header-item a{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.header-item .fa{font-size:21px;line-height:24px}.header-item .button+.button,.header-item .pagination a+.button,.pagination .header-item a+.button,.header-item .pagination .button+a,.pagination .header-item .button+a,.header-item .pagination a+a,.pagination .header-item a+a{margin-left:10px}@media screen and (max-width: 768px){.header-item{text-align:left}}.header-item a,a.header-item{color:#69707a}.header-item a:hover,a.header-item:hover{color:#222324}.header-item a.is-active,a.header-item.is-active{color:#222324}.header-icon{display:inline-block;font-size:14px;height:24px;line-height:24px;text-align:center;vertical-align:top;width:24px;color:#69707a;margin:0 5px}.header-icon:hover{color:#222324}.header-tab{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;border-bottom:1px solid transparent;color:#69707a;display:block;height:50px;line-height:24px;padding:13px 15px}.header-tab:hover{border-bottom:1px solid #1fc8db}.header-tab.is-active{border-bottom:3px solid #1fc8db;color:#1fc8db}.header-left{-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;overflow:hidden;overflow-x:auto;white-space:nowrap}@media screen and (max-width: 768px){.header-left{height:50px}}@media screen and (min-width: 980px){.header-left .header-item:first-child{padding-left:0}}.header-right{-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;-ms-grid-row-align:stretch;align-items:stretch}@media screen and (min-width: 769px){.header-right{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}}@media screen and (min-width: 980px){.header-right .header-item:last-child{padding-right:0}}.header-full{-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;width:100%}.header-full>.header-item{-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;padding:0}.header-full>.header-item>a{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;width:100%}@media screen and (max-width: 768px){.header-menu{box-shadow:0 4px 7px rgba(0,0,0,0.1);display:none}.header-menu .header-item{border-top:1px solid rgba(211,214,219,0.5);padding:10px}.header-menu.is-active{display:block}}.header.is-centered{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.header.is-centered .header-left,.header.is-centered .header-right{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.header.is-small{background:#f5f7fa;box-shadow:none;height:40px;z-index:1}.header.is-small .container{height:40px}.header.is-small .header-tab{font-size:13px;height:40px;padding:8px 10px}.header.is-small .header-tab:hover,.header.is-small .header-tab.is-active{border-bottom-width:2px}.hero-video{bottom:0;left:0;position:absolute;right:0;top:0;overflow:hidden}.hero-video.is-transparent{opacity:0.3}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;-webkit-transform:translate3d(-50%, -50%, 0);transform:translate3d(-50%, -50%, 0)}@media screen and (max-width: 768px){.hero-video{display:none}}.hero-content{padding:40px 20px}@media screen and (min-width: 980px){.hero-content{padding:40px 0}}.hero-buttons{margin-top:20px}@media screen and (max-width: 768px){.hero-buttons .button,.hero-buttons .pagination a,.pagination .hero-buttons a{display:block}.hero-buttons .button:not(:last-child),.hero-buttons .pagination a:not(:last-child),.pagination .hero-buttons a:not(:last-child){margin-bottom:10px}}@media screen and (min-width: 769px){.hero-buttons{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.hero-buttons .button:not(:last-child),.hero-buttons .pagination a:not(:last-child),.pagination .hero-buttons a:not(:last-child){margin-right:20px}}.hero{background:white;text-align:center}.hero .header{background:none}.hero .header .container{box-shadow:0 1px 0 rgba(211,214,219,0.3)}.hero .tabs a{border:none}.hero .tabs ul{border-bottom:none}.hero .tabs.is-boxed a{padding:8px 15px}.hero.is-dark{background:#222324;color:#fff}.hero.is-dark .title{color:#fff}.hero.is-dark .title a,.hero.is-dark .title strong{color:inherit}.hero.is-dark .subtitle{color:rgba(255,255,255,0.7)}.hero.is-dark .subtitle strong{color:#fff}.hero.is-dark .header .container{box-shadow:0 1px 0 rgba(255,255,255,0.2)}.hero.is-dark .header-icon,.hero.is-dark a.header-item,.hero.is-dark .header-item>a:not(.button){color:rgba(255,255,255,0.5)}.hero.is-dark .header-icon:hover,.hero.is-dark .header-icon.is-active,.hero.is-dark a.header-item:hover,.hero.is-dark a.header-item.is-active,.hero.is-dark .header-item>a:not(.button):hover,.hero.is-dark .header-item>a:not(.button).is-active{color:#fff}.hero.is-dark .tabs a{color:#fff;opacity:0.5}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#fff}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background:rgba(0,0,0,0.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background:#fff;color:#222324}.hero.is-dark.is-bold{background-image:-webkit-linear-gradient(309deg, #080a0b 0%, #222324 71%, #2c2e34 100%);background-image:linear-gradient(141deg, #080a0b 0%, #222324 71%, #2c2e34 100%)}@media screen and (max-width: 768px){.hero.is-dark .header-toggle span{background:#fff}.hero.is-dark .header-toggle:hover{background:rgba(0,0,0,0.1)}.hero.is-dark .header-toggle.is-active span{background:#fff}.hero.is-dark .header-menu .header-item{border-top-color:rgba(255,255,255,0.2)}}.hero.is-primary{background:#1fc8db;color:#fff}.hero.is-primary .title{color:#fff}.hero.is-primary .title a,.hero.is-primary .title strong{color:inherit}.hero.is-primary .subtitle{color:rgba(255,255,255,0.7)}.hero.is-primary .subtitle strong{color:#fff}.hero.is-primary .header .container{box-shadow:0 1px 0 rgba(255,255,255,0.2)}.hero.is-primary .header-icon,.hero.is-primary a.header-item,.hero.is-primary .header-item>a:not(.button){color:rgba(255,255,255,0.5)}.hero.is-primary .header-icon:hover,.hero.is-primary .header-icon.is-active,.hero.is-primary a.header-item:hover,.hero.is-primary a.header-item.is-active,.hero.is-primary .header-item>a:not(.button):hover,.hero.is-primary .header-item>a:not(.button).is-active{color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:0.5}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background:rgba(0,0,0,0.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background:#fff;color:#1fc8db}.hero.is-primary.is-bold{background-image:-webkit-linear-gradient(309deg, #0fb8ad 0%, #1fc8db 71%, #2cb5e8 100%);background-image:linear-gradient(141deg, #0fb8ad 0%, #1fc8db 71%, #2cb5e8 100%)}@media screen and (max-width: 768px){.hero.is-primary .header-toggle span{background:#fff}.hero.is-primary .header-toggle:hover{background:rgba(0,0,0,0.1)}.hero.is-primary .header-toggle.is-active span{background:#fff}.hero.is-primary .header-menu .header-item{border-top-color:rgba(255,255,255,0.2)}}.hero.is-info{background:#42afe3;color:#fff}.hero.is-info .title{color:#fff}.hero.is-info .title a,.hero.is-info .title strong{color:inherit}.hero.is-info .subtitle{color:rgba(255,255,255,0.7)}.hero.is-info .subtitle strong{color:#fff}.hero.is-info .header .container{box-shadow:0 1px 0 rgba(255,255,255,0.2)}.hero.is-info .header-icon,.hero.is-info a.header-item,.hero.is-info .header-item>a:not(.button){color:rgba(255,255,255,0.5)}.hero.is-info .header-icon:hover,.hero.is-info .header-icon.is-active,.hero.is-info a.header-item:hover,.hero.is-info a.header-item.is-active,.hero.is-info .header-item>a:not(.button):hover,.hero.is-info .header-item>a:not(.button).is-active{color:#fff}.hero.is-info .tabs a{color:#fff;opacity:0.5}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background:rgba(0,0,0,0.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background:#fff;color:#42afe3}.hero.is-info.is-bold{background-image:-webkit-linear-gradient(309deg, #13bfdf 0%, #42afe3 71%, #53a1eb 100%);background-image:linear-gradient(141deg, #13bfdf 0%, #42afe3 71%, #53a1eb 100%)}@media screen and (max-width: 768px){.hero.is-info .header-toggle span{background:#fff}.hero.is-info .header-toggle:hover{background:rgba(0,0,0,0.1)}.hero.is-info .header-toggle.is-active span{background:#fff}.hero.is-info .header-menu .header-item{border-top-color:rgba(255,255,255,0.2)}}.hero.is-success{background:#97cd76;color:#fff}.hero.is-success .title{color:#fff}.hero.is-success .title a,.hero.is-success .title strong{color:inherit}.hero.is-success .subtitle{color:rgba(255,255,255,0.7)}.hero.is-success .subtitle strong{color:#fff}.hero.is-success .header .container{box-shadow:0 1px 0 rgba(255,255,255,0.2)}.hero.is-success .header-icon,.hero.is-success a.header-item,.hero.is-success .header-item>a:not(.button){color:rgba(255,255,255,0.5)}.hero.is-success .header-icon:hover,.hero.is-success .header-icon.is-active,.hero.is-success a.header-item:hover,.hero.is-success a.header-item.is-active,.hero.is-success .header-item>a:not(.button):hover,.hero.is-success .header-item>a:not(.button).is-active{color:#fff}.hero.is-success .tabs a{color:#fff;opacity:0.5}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background:rgba(0,0,0,0.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background:#fff;color:#97cd76}.hero.is-success.is-bold{background-image:-webkit-linear-gradient(309deg, #8ecb45 0%, #97cd76 71%, #96d885 100%);background-image:linear-gradient(141deg, #8ecb45 0%, #97cd76 71%, #96d885 100%)}@media screen and (max-width: 768px){.hero.is-success .header-toggle span{background:#fff}.hero.is-success .header-toggle:hover{background:rgba(0,0,0,0.1)}.hero.is-success .header-toggle.is-active span{background:#fff}.hero.is-success .header-menu .header-item{border-top-color:rgba(255,255,255,0.2)}}.hero.is-warning{background:#fce473;color:rgba(0,0,0,0.5)}.hero.is-warning .title{color:rgba(0,0,0,0.5)}.hero.is-warning .title a,.hero.is-warning .title strong{color:inherit}.hero.is-warning .subtitle{color:rgba(0,0,0,0.7)}.hero.is-warning .subtitle strong{color:rgba(0,0,0,0.5)}.hero.is-warning .header .container{box-shadow:0 1px 0 rgba(0,0,0,0.2)}.hero.is-warning .header-icon,.hero.is-warning a.header-item,.hero.is-warning .header-item>a:not(.button){color:rgba(0,0,0,0.5)}.hero.is-warning .header-icon:hover,.hero.is-warning .header-icon.is-active,.hero.is-warning a.header-item:hover,.hero.is-warning a.header-item.is-active,.hero.is-warning .header-item>a:not(.button):hover,.hero.is-warning .header-item>a:not(.button).is-active{color:rgba(0,0,0,0.5)}.hero.is-warning .tabs a{color:rgba(0,0,0,0.5);opacity:0.5}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,0.5)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background:rgba(0,0,0,0.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background:rgba(0,0,0,0.5);color:#fce473}.hero.is-warning.is-bold{background-image:-webkit-linear-gradient(309deg, #ffbd3d 0%, #fce473 71%, #fffe8a 100%);background-image:linear-gradient(141deg, #ffbd3d 0%, #fce473 71%, #fffe8a 100%)}@media screen and (max-width: 768px){.hero.is-warning .header-toggle span{background:rgba(0,0,0,0.5)}.hero.is-warning .header-toggle:hover{background:rgba(0,0,0,0.1)}.hero.is-warning .header-toggle.is-active span{background:rgba(0,0,0,0.5)}.hero.is-warning .header-menu .header-item{border-top-color:rgba(0,0,0,0.2)}}.hero.is-danger{background:#ed6c63;color:#fff}.hero.is-danger .title{color:#fff}.hero.is-danger .title a,.hero.is-danger .title strong{color:inherit}.hero.is-danger .subtitle{color:rgba(255,255,255,0.7)}.hero.is-danger .subtitle strong{color:#fff}.hero.is-danger .header .container{box-shadow:0 1px 0 rgba(255,255,255,0.2)}.hero.is-danger .header-icon,.hero.is-danger a.header-item,.hero.is-danger .header-item>a:not(.button){color:rgba(255,255,255,0.5)}.hero.is-danger .header-icon:hover,.hero.is-danger .header-icon.is-active,.hero.is-danger a.header-item:hover,.hero.is-danger a.header-item.is-active,.hero.is-danger .header-item>a:not(.button):hover,.hero.is-danger .header-item>a:not(.button).is-active{color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:0.5}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background:rgba(0,0,0,0.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background:#fff;color:#ed6c63}.hero.is-danger.is-bold{background-image:-webkit-linear-gradient(309deg, #f32a3e 0%, #ed6c63 71%, #f39376 100%);background-image:linear-gradient(141deg, #f32a3e 0%, #ed6c63 71%, #f39376 100%)}@media screen and (max-width: 768px){.hero.is-danger .header-toggle span{background:#fff}.hero.is-danger .header-toggle:hover{background:rgba(0,0,0,0.1)}.hero.is-danger .header-toggle.is-active span{background:#fff}.hero.is-danger .header-menu .header-item{border-top-color:rgba(255,255,255,0.2)}}@media screen and (min-width: 769px){.hero.is-fullheight .tabs,.hero.is-large .tabs{font-size:18px}}@media screen and (min-width: 769px){.hero.is-medium .hero-content{padding:120px 20px}}@media screen and (min-width: 980px){.hero.is-medium .hero-content{padding:120px 0}}.hero.is-large .tabs a{padding:10px 15px}@media screen and (min-width: 769px){.hero.is-large .hero-content{padding:240px 20px}}@media screen and (min-width: 980px){.hero.is-large .hero-content{padding:240px 0}}.hero.is-fullheight{-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;min-height:100vh}.hero.is-fullheight .tabs a{padding:15px 20px}.hero.is-fullheight .hero-content{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.hero.is-left{text-align:left}.hero.is-right{text-align:right}.section{background:white;padding:40px 20px}.section+.section{border-top:1px solid rgba(211,214,219,0.5)}@media screen and (min-width: 980px){.section{padding:40px 0}.section.is-medium{padding:120px 0}.section.is-large{padding:240px 0}}.footer{background:#f5f7fa;padding:40px 20px 80px}.footer a{color:#69707a}.footer a:hover{color:#222324}.footer a:not(.icon){border-bottom:1px solid #d3d6db}.footer a:not(.icon):hover{border-bottom-color:#1fc8db} +/*# sourceMappingURL=bulma.min.css.map */ \ No newline at end of file diff --git a/homie-esp8266-v1-setup-sources/app/vendor/font-awesome/icons.min.css b/homie-esp8266-v1-setup-sources/app/vendor/font-awesome/icons.min.css new file mode 100644 index 0000000..f384095 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/app/vendor/font-awesome/icons.min.css @@ -0,0 +1 @@ +@font-face{font-family:'FortAwesome';src:url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAA7kAA8AAAAAGfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABWAAAABsAAAAceQ+rm09TLzIAAAF0AAAARQAAAGAQ+ZFmY21hcAAAAbwAAABRAAABWuAdGx9jdnQgAAAILAAAAAsAAAAOAAAAAGZwZ20AAAg4AAAGPAAADRZ2ZHx0Z2FzcAAACCQAAAAIAAAACAAAABBnbHlmAAACEAAAA/cAAAYYwbvfm2hlYWQAAAYIAAAAMAAAADYKPbaJaGhlYQAABjgAAAAeAAAAJAgkBR1obXR4AAAGWAAAACcAAAAwJDQARmxvY2EAAAaAAAAAGQAAABoFkASObWF4cAAABpwAAAAgAAAAIADADf9uYW1lAAAGvAAAAR4AAAIfHNKRPHBvc3QAAAfcAAAASAAAAHJT8CZ3cHJlcAAADnQAAABtAAAAgicVCkB42mNgYGBkAIJLkRbnQfTlaMtUKJ0GAEGwBc0AeNpjYGHewTiBgZWBgamf6SADA0MvhGZ8zGDEyAIUZWBlZoABRgEGNNDAwPCBlTnhfwFDNHMCwwQglxFJVoGBEQBPjQr4AAAAeNpjYGBgZoBgGQZGBhAIAfIYwXwWBgsgzcXAwcAEhAwMvAwKH1j//werArEZgGzm/9//PxVgg+qFAkY2iJFgNkgnCwMqAKpmZRjeAACckwswAAAAeNqdVE1sG0UUnpndmdmdjXez9u46P07irO11sZ3EWTu7qEltpyUhSUVBrpecQBxAStKohx4qNap6CD1QFamVOKFKUQpC4oByqED0UIkLUg/lEIlG8pEDBy6lwAkJ6jJjagqlh8DO6pvve/tm5u28NwMQGAcAFtAukAAFyhdEhgCVixVz3MxXzMw4VH+4exftPnprHL0J+IMAeHxb+kkaADaY4P5HRnXM/WGFUEjcPHTzFb8OE44fJpJOzqKZvEcMKHkulTLeTDXwk4HvoP3PVLnzERtlnZtQ20/R/Bk1IM00g8HR/RrzyfJZJaTzPx699nBr6+E1mFalzh5j8LQ2vNO5MjQEzbr1c3P1ooOTv8DtoeHOb6vNi1e3tq524+P/s8bjGwWTPL6Ca0s8vpxtkcy46x2D1WAWzmR6zHdss+I7Ev9chE+NcM02Ot8Ztm1kswLbhs37Jdu4LhSHzSVBOLS7yL3v2OjCpmACAJDBAKCyiEMFI6ACFkCsweZnpsaSDAO5XMy63d1wbCt0J6GrQx5SNazxtZOORQK+lZPcRP9hF1YvTYmwBNm0nHTQSae5UF6YhcWl5ZeLkEWkdHn7colED1omfuXd6I3VpSpkLbxyv31/BX/Y6qf3Oql7tL+FN6AL+bobmJ7W4oODcW0xxZ9WHJemp0s43nrQwrNhIfsCH7LSbK7QKxG+dQtH/XT93Ll1CgD+qw4MMAamQB28CoxG38kTs5ViZjihSbhcDJ/Z1X9p8089Yz5fJ57R15myqbAutP8DDXqENT4XTMB7nTuCwpc47j/6XnCU4ggHfv9aCGmOoyglwv/zgvyitMpPhwGS4AzYBQfAbOhffrx9tlVw4jojiJSLoOq5xHJ8+Ukff9JT2zIgoY6lQ3eKE96Kgnl5nsdJuVqDoccV35I6DEKPa3+ME95mBXOSYeCPynw0dYQKQt/xLcqLYQRaVMwrZsy4eV4aJJN3q6GXF5OFYi4/7B63xP8ZBH+NzkfReYQLc4XeuxdbpVqYOG7i+fggJXJszUAxxzquqGrScnTiqTqO9Oc7nSDs7043SxpuSCMaabWINoIks4xomTikTBdRzyolJvqE7fCucELEHO1140Ub3e4Tsb7tLPQNzmOfKceGYvrrCqvIPDEDcaZMx0b7Y8g4jNO3c/IRnnhDYac0auiLDEGWakDY6FmZYugNUxtpQFQ/vK+4s2KPt6VP+VmioCju1DhB6GlJhRbPlgFFHYnCqUNROl0SVHjKalBcYIV0KZ0u7URv55Y1tU1UmqXS1M4NyGiW0UuYHdRyG199oGjolPBLt9//5p1c7YDhSwhnVfXGzhTEWdbXVrXl3PprGvgDV5j7QAB42mNgZGBgAOLznZVy8fw2Xxm4mT8ARRguR1umIej/lSwZzOFALgcDE0gUADWbCnF42mNgZGBgTmCYwBDN0sAABCwZDIwMqIAHADs9Ak0AAHjaY8xhUGQAAsYABgbmD6iYMYOBgaWBQQLEZrEAiq1g4AUAs3IHmAB42mNgYJBAghkMaxhlGCcwHWLmAQAawAMmAAAAAAEAAAAMANYABgAAAAAAAgAAABEAiwAAACINFgAAAAB42nWPMU+DQBiGX1qoMRrHamJibrMdIBwNHdqlHexoTIcuTgyUkrScgaMd/Bn+ChdX/54v3CViUiFv7vm+7+GOA3CDLzgwzzVj2MEVK8M9XODWch+PeLDs0nmy7EHgxfKA/TeajnvJ6g7vlh0M8WG5x3M/LffxjG/LLoaOZ9nDwrm3PGD/daVKLZantFKHtGGL6zSr90m5UoVmN0tFFIRiJjoGKznxYz8K5bTT3qRllatCSPpzofU2qbXa5YUWo6MM4nFHxQoKJTSvucQJKSrWB/z2/3bXTIYaeyScNk5Bx7gZZwIRAoRcZ8z5PcxMYgIfMRPRl5j+Y2+YklXentV8Z/afM5rvln9Sc1XY0Snam4xwbL0Y4/O7/gDQ0luCAAB42mNgYgCD/00MRgzYAA8QMzIwMTIxMjMwM7IwsjKyMbIzcjBysviF+vgwOQexl+ZluhkYGEBpQyhtBKWNobQJlDYFAGghENcAAQAB//8AD3jaY2BABgAADgABAHjarVZpd9NGFJW8ZSMbWWhRS8dMnKbRyKQUggEDQYrtQro4WytBaaU4SfcFutF9X/CveXLac+g3flrvG9kmgYSe9tQf9O7MuzNvm3ljMpQgY92vBEIs3TWGlpcot3rNp1MWzQThtmiu+5QqRH/1Gr1GoyE3rHyejIAMTy62DNPwQtchU5EItx1KKbEp6F6dMtPXWjNmv1dpVChX8fOULgQr1/28zFtNX1C9jqmFwBJUYlQKAhEn7GiTZjDVHgmaY/0cM+/VfQFvmpGg/rofYkawrp/RPKP50AqDILDItINAklH3t4LAobQS2CdTiOBZ1qv7lJUu5aSLOAIyQ4cySsIvsRlnN1zBGvbYSjzgLxlhpUHp2TyUnmiKJgzEc9kCglz2w7oVrQS+DPKBoIVVHzqLQ2vbdyirqMezW0YqyVQOQ+lKZFy6EaU2tslswAvKzjrUowS7OuA17maMDcE70EIYMCVc1K72qlbPgOFV3Nl8N/d9am8t+pNdTBsueIg7FJWmjLguOl+GxTklYcHJjpeojowWExMDByynKawyrPuh7V50SOmAWgP9aRTbkvlgNu/QoIpTqQptRosODSkQhaBD3lVeDiDdgAZ5tILRIEYODWObEZ0SgQw0YJeGvFA0Q0FDSJpDI2ppzY8zm4vBFA1uydsOjaqlZX9pNZm08pgf0/OHVWwMe+t+PDzskRm5NGzzmcVJduND/BnEh8xJVCJdqPsxJw/Ruk3Ul83O5iWWdbCV6HkJrgLPBIikBv9rmN1bqgMKGBvGmES2cJwutkzT1LUaU0ZspCprPg1LV1RoAIevH/kNXRHC/J+jo6YxZLhuM4wP52y6Y1vHkaZxxDZmOzShYpPlJPLM8oiK0ywfU3GG5eMqzrI8quIcS0vFPSyfUHEvyydV3MfyGSU7eadciAxLUSTzBl8Qh2Z3KSe7ypuJ0t6lnO4qbyXKY8qgQfs/xPcU4jsGvwTiY5lHfCyPIz6WEvGxnEJ8LAuIj+U04mP5NOJjOYP4WColyvqYOgpmR0Phobahp0uJq6f4rBYVOTY5uIUncAFq4oAqyqgkuSM+kmFx9HPd0pqTdGI2zpoTFR+NjAN8dndmHlafVOK09vc58MzKw0ZwO/c1zvPG5B8G/xYvylJ80pzg4E4hAfB4f4dxK6KSQ6dV8UjZofl/ouIEN0A/g5oYkwVRFDW++cjllWazJmtoFT6eCHRWtIN505wYR0pLaFGTNAJaBl2zoGnU59lbzaIUotzEfmf3UkQx2YsymAFTUMhNY2HZ30mJtLB2UtPpo4HLjbQXPVlqtqziCnsP3seQm1nybqS8cFNS2os2oU55kQUcciN7cE0Et9DeZRXFlLBQRXwQ2gr228eITFpmBl0CRcjiZGUf2hU7ckQF7QS+9aRV3reF2p/r5EFgNjvdzoMsI0Xnuyrq1fqqrLFRrl65mz4Opp1hY80vijKeXPa+PSnYr04JcgWMrux+3ZPi7Xes25WSfLYv7PLE65Qq5L8AD4bcKe9FNIoiZ7FKI55ft/BkinJQjIvmOC7opT3aFau+R7uw79pHrbisqGQ/yqCr6KzdhG98vhDUgVQUtEhFrPB0yHw2OzXhYylxWYq4Z8mui+g7eEI6xH9xiGv/17nlKLhFlSW60K4Tkg/aPlbQW0t2Jw9VjM7aednORDuSbtA1BD2RXHD8vcBdHivSKdzn5w+Yv4LtzPExOg18VdEZiCXOWwUJFlW8pZ1MvaD4CNMS4IuqhWYF8BKAyeBl1TL1TB1AzywzpwKwwhwGq8xhsMYcButqB13vMtArQKZGr6odM5nzgZK5gHkmo2vM0+g68zR6jXka3WCbHsDrbJPBG2yTQcg2GUTMqQJsMIdBgzkMNpnDYEv75QJta78Yvan9YvSW9ovR29ovRu9ovxi9q/1i9J72i9H7yPG5bgE/0CO6CPhhAi8BfsRJ16MFjG7iGW1zbiWQOR9rjtnmfILF57u7fqpHesVnCeQVnyeQ6bexT5vwRQKZ8GUCmfAVuOXufl/rkaZ/k0Cmf5tApn+HlW3C9wlkwg8JZMKP4F7o7veTHmn6zwlk+i8JZPqvWNkm/JZAJvyeQCbcUTt9mVTnz6prU+8WpafqtzvvsPM3Rxm6KXjaY/DewXAiKGIjI2Nf5AbGnRwMHAzJBRsZ2J02MjBoQWgOFHonAwMDJzKLmcFlowpjR2DEBoeOiI3MKS4b1UC8XRwNDIwsDh3JIREgJZFAsJGBR2sH4//WDSy9G5kYXDazprAxuLgAAAZPJGwAAAA=) format("woff");font-weight:400;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FortAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-heartbeat:before{content:'\f000'}.fa-info:before{content:'\f001'}.fa-wifi:before{content:'\f002'}.fa-signal:before{content:'\f003'}.fa-cogs:before{content:'\f004'}.fa-rocket:before{content:'\f005'} diff --git a/homie-esp8266-v1-setup-sources/gulpfile.babel.js b/homie-esp8266-v1-setup-sources/gulpfile.babel.js new file mode 100644 index 0000000..e8e17bb --- /dev/null +++ b/homie-esp8266-v1-setup-sources/gulpfile.babel.js @@ -0,0 +1,163 @@ +'use strict'; + +import gulp from 'gulp'; +import plumber from 'gulp-plumber'; // help to avoid crash if error in a task +import newer from 'gulp-newer'; +import watch from 'gulp-watch'; // gulp.watch doesn't detect new files +import smoosher from 'gulp-smoosher'; +import zopfli from 'gulp-zopfli'; +import rename from 'gulp-rename'; +import del from 'del'; // delete files +import imagemin from 'gulp-imagemin'; +import runSequence from 'run-sequence'; +import notifier from 'node-notifier'; +import uglify from 'gulp-uglify'; +import browserify from 'browserify'; +import envify from 'envify/custom'; +import babelify from 'babelify'; +import source from 'vinyl-source-stream'; // helper for browserify text stream to gulp pipeline +import buffer from 'vinyl-buffer'; // helper for browserify + +let errored = false; +let errorHandler = function (task) { + return function (error) { + errored = true; + console.log(`Error in ${task}: ${error.message}`); + notifier.notify({ + title: `Error in ${task}`, + message: error.message + }); + }; +}; + +// ################ +// # Entry points # +// ################ + +gulp.task('dev', ['buildpublic:dev'], function (done) { + watch('./app/assets/**/*', function (vinyl) { + console.log(`${vinyl.path} was ${vinyl.event}, piping to public/...`); + runSequence('assets'); + }); + + watch('./app/vendor/**/*', function (vinyl) { + console.log(`${vinyl.path} was ${vinyl.event}, piping to public/vendor/...`); + runSequence('vendor'); + }); + + watch('./app/js/**/*.js', function (vinyl) { + console.log(`${vinyl.path} was '${vinyl.event}', running Babel...`); + runSequence('es6-7:dev'); + }); +}); + +gulp.task('dist', function (done) { + runSequence('buildpublic:dist', 'builduigz', function () { + if (errored) { + console.log('Distribution failed'); + process.exit(-1); + } + + done(); + }); +}); + +// #################### +// # public directory # +// #################### + +gulp.task('builduigz', function () { + return gulp.src('index.html') + .pipe(plumber(errorHandler('buildpublic:imagemin'))) + .pipe(smoosher()) + .pipe(rename('ui_bundle')) + .pipe(zopfli()) + .pipe(gulp.dest('./')); +}); + +gulp.task('buildpublic:dist', function (done) { + runSequence( + 'buildpublic:clear', + ['assets', 'vendor', 'es6-7:dist'], + 'buildpublic:imagemin', + done); +}); + +gulp.task('buildpublic:dev', function (done) { + runSequence( + 'buildpublic:clear', + ['assets', 'vendor', 'es6-7:dev'], + done); +}); + +gulp.task('buildpublic:clear', function (done) { + del(['./public/**/*']).then(function () { + done(); + }); +}); + +gulp.task('buildpublic:imagemin', function () { + return gulp.src('./public/img/**/*.{png,jpg,gif,svg}', { + base: './public' + }) + .pipe(plumber(errorHandler('buildpublic:imagemin'))) + .pipe(imagemin({ + progressive: true + })) + .pipe(gulp.dest('./public')); +}); + +// Babel + +let es67 = (prod = false) => { + const babelPlugins = []; + if (prod) babelPlugins.push(['module-alias', [{ 'src': 'npm:preact-compat', 'expose': 'react' }, { 'src': 'npm:preact-compat', 'expose': 'react-dom' }]]); + return browserify({ entries: './app/js/app.js', debug: false }) // debug for sourcemaps + .transform(babelify, { presets: ['es2015', 'stage-3', 'react'], plugins: babelPlugins }); +}; + +gulp.task('es6-7:dev', function () { + return es67() + .bundle() + .on('error', function (error) { + errorHandler('es6-7')(error); + this.emit('end'); + }) // Don't crash if failed, plumber doesn't work with browserify + .pipe(source('bundle.min.js')) + .pipe(buffer()) + .pipe(gulp.dest('./public/js')); +}); + +gulp.task('es6-7:dist', function () { + return es67(true) + .transform(envify({ NODE_ENV: 'production' }), { global: true }) // global: act on node_modules (here react prod mode) + .bundle() + .on('error', function (error) { + errorHandler('es6-7')(error); + this.emit('end'); + }) // Don't crash if failed, plumber doesn't work with browserify + .pipe(source('bundle.min.js')) + .pipe(buffer()) + .pipe(uglify()) + .pipe(gulp.dest('./public/js/')); +}); + +// assets and vendor + +gulp.task('assets', function () { + return gulp.src('./app/assets/**/*', { + base: './app/assets' + }) + .pipe(plumber(errorHandler('assets'))) + .pipe(newer('./public')) + .pipe(gulp.dest('./public')); +}); + +gulp.task('vendor', function () { + return gulp.src('./app/vendor/**/*', { + base: './app' + }) + .pipe(plumber(errorHandler('vendor'))) + .pipe(newer('./public')) + .pipe(gulp.dest('./public')); +}); diff --git a/homie-esp8266-v1-setup-sources/index.html b/homie-esp8266-v1-setup-sources/index.html new file mode 100644 index 0000000..3cbf2f2 --- /dev/null +++ b/homie-esp8266-v1-setup-sources/index.html @@ -0,0 +1,34 @@ + + + + + + + + Set up your Homie for ESP8266 device + + + + + + + + + + + + +
+
+ + + + + + diff --git a/homie-esp8266-v1-setup-sources/package.json b/homie-esp8266-v1-setup-sources/package.json new file mode 100644 index 0000000..dd7a23c --- /dev/null +++ b/homie-esp8266-v1-setup-sources/package.json @@ -0,0 +1,55 @@ +{ + "name": "homie-esp8266-configurator", + "version": "0.1.0", + "description": "UI to configure an ESP8266 loaded with an Homie firmware", + "scripts": { + "dev": "gulp dev", + "dist": "gulp dist" + }, + "author": { + "name": "Marvin Roger", + "email": "bonjourmarvin@marvinroger.fr", + "url": "http://marvinroger.fr" + }, + "repository": { + "type": "git", + "url": "https://github.com/marvinroger/homie-esp8266-configurator.git" + }, + "bugs": "https://github.com/marvinroger/homie-esp8266-configurator/issues", + "homepage": "https://github.com/marvinroger/homie-esp8266-configurator", + "license": "GPL-2.0", + "dependencies": { + "babel-runtime": "^6.3.19" + }, + "devDependencies": { + "babel-core": "^6.4.0", + "babel-plugin-module-alias": "^1.4.0", + "babel-plugin-transform-runtime": "^6.3.13", + "babel-preset-es2015": "^6.1.18", + "babel-preset-react": "^6.1.18", + "babel-preset-stage-3": "^6.1.18", + "babelify": "^7.2.0", + "browserify": "^13.0.0", + "del": "^2.2.0", + "envify": "^3.4.0", + "gulp": "^3.9.0", + "gulp-imagemin": "^2.4.0", + "gulp-newer": "^1.1.0", + "gulp-plumber": "^1.0.1", + "gulp-rename": "^1.2.2", + "gulp-smoosher": "0.0.9", + "gulp-uglify": "^1.5.1", + "gulp-watch": "^4.3.5", + "gulp-zopfli": "^1.0.0", + "immutable": "^3.7.6", + "node-notifier": "^4.3.1", + "preact-compat": "^1.7.1", + "react": "^15.0.1", + "react-dom": "^15.0.1", + "request": "^2.67.0", + "run-sequence": "^1.1.5", + "vinyl-buffer": "^1.0.0", + "vinyl-source-stream": "^1.1.0", + "whatwg-fetch": "^0.11.0" + } +} diff --git a/pixelprojektor/pixelprojektor.ino b/pixelprojektor/pixelprojektor.ino index 72b9dfd..311fb12 100644 --- a/pixelprojektor/pixelprojektor.ino +++ b/pixelprojektor/pixelprojektor.ino @@ -1,24 +1,50 @@ -#include - +#include // homie lib from: https://github.com/marvinroger/homie-esp8266/ +#include +#ifdef __AVR__ + #include +#endif +#define PIN 2 //data pin for ws2812 -HomieNode effectNode("effect", "commands"); -HomieNode pixelsNode("pixels", "commands"); +Adafruit_NeoPixel strip = Adafruit_NeoPixel(64, PIN, NEO_GRB + NEO_KHZ800); +HomieNode homieNode("pixel", "commands"); + +void led_fill(uint32_t c) +{ + for (int i=0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, c); //turn every third pixel on + } + strip.show(); +} bool effectHandler(const HomieRange& range, const String& value) { int sep = value.indexOf("|"); - if(sep > 0) { - //Homie.getLogger() << "scroll " << value << " wait " << wait << endl; - - } else { + Homie.getLogger() << "-> " << value << endl; + + //Serial.print("->"); Serial.println(value); + + if (value.charAt(0)=='#'){ //solid fill + String color=value.substring(1); + int number = (int) strtol( &color[0], NULL, 16); - //Homie.getLogger() << "scroll " << value << endl; + + // Split them up into r, g, b values + int r = number >> 16; + int g = number >> 8 & 0xFF; + int b = number & 0xFF; + Homie.getLogger() << "r=" << r << " g=" << g << " b=" << b << endl; + //Serial.print("r=");Serial.print(r); + //Serial.print(" g=");Serial.print(g); + //Serial.print(" b=");Serial.println(b); + + led_fill(strip.Color(r, g, b)); } + return true; } @@ -46,11 +72,17 @@ void setup() { Homie_setFirmware("pixelprojektor", "1.0.0"); - effectNode.advertise("on").settable(effectHandler); - pixelsNode.advertise("on").settable(pixelsHandler); + homieNode.advertise("effect").settable(effectHandler); + homieNode.advertise("pixels").settable(pixelsHandler); + strip.begin(); + strip.show(); // Initialize all pixels to 'off' + + led_fill(strip.Color(0, 0, 0)); Homie.setup(); + + } void loop() { @@ -61,3 +93,6 @@ void loop() { + + +