From e900f5e2a2db4ea7d45f801a4d48a879975f2064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 8 May 2018 14:03:56 +0200 Subject: [PATCH] Add example structure --- doc/examples/examples.pro | 4 + doc/examples/template/LICENSE | 503 +++++++++++++++ doc/examples/template/README.md | 4 + doc/examples/template/debian/changelog | 5 + doc/examples/template/debian/compat | 1 + doc/examples/template/debian/control | 22 + doc/examples/template/debian/copyright | 14 + .../debian/nymea-plugin-template.install.in | 1 + doc/examples/template/debian/rules | 25 + doc/examples/template/debian/source/format | 1 + .../template/deviceplugintemplate.cpp | 67 ++ doc/examples/template/deviceplugintemplate.h | 54 ++ .../template/deviceplugintemplate.json | 39 ++ doc/examples/template/template.pro | 14 + doc/generate-api-qdoc.py | 2 + doc/getting-started-plugins.qdoc | 214 +------ doc/jsonrpc-api.qdoc | 4 +- doc/jsonrpc.qdoc | 6 +- doc/libnymea.qdoc | 2 - doc/nymea.qdoc | 2 +- doc/plugin-tutotrials.qdoc | 7 + doc/plugin-wizard.qdoc | 14 + doc/setup-environment.qdoc | 44 +- doc/tutorial1.qdoc | 145 ----- doc/tutorial2.qdoc | 482 -------------- doc/tutorial3.qdoc | 141 ----- doc/tutorial4.qdoc | 122 ---- doc/tutorial5.qdoc | 590 ------------------ doc/tutorial6.qdoc | 434 ------------- doc/tutorials.qdoc | 11 - doc/write-plugins.qdoc | 35 +- 31 files changed, 812 insertions(+), 2197 deletions(-) create mode 100644 doc/examples/examples.pro create mode 100644 doc/examples/template/LICENSE create mode 100644 doc/examples/template/README.md create mode 100644 doc/examples/template/debian/changelog create mode 100644 doc/examples/template/debian/compat create mode 100644 doc/examples/template/debian/control create mode 100644 doc/examples/template/debian/copyright create mode 100644 doc/examples/template/debian/nymea-plugin-template.install.in create mode 100644 doc/examples/template/debian/rules create mode 100644 doc/examples/template/debian/source/format create mode 100644 doc/examples/template/deviceplugintemplate.cpp create mode 100644 doc/examples/template/deviceplugintemplate.h create mode 100644 doc/examples/template/deviceplugintemplate.json create mode 100644 doc/examples/template/template.pro create mode 100644 doc/plugin-tutotrials.qdoc create mode 100644 doc/plugin-wizard.qdoc delete mode 100644 doc/tutorial1.qdoc delete mode 100644 doc/tutorial2.qdoc delete mode 100644 doc/tutorial3.qdoc delete mode 100644 doc/tutorial4.qdoc delete mode 100644 doc/tutorial5.qdoc delete mode 100644 doc/tutorial6.qdoc delete mode 100644 doc/tutorials.qdoc diff --git a/doc/examples/examples.pro b/doc/examples/examples.pro new file mode 100644 index 00000000..13c36e92 --- /dev/null +++ b/doc/examples/examples.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +CONFIG += no_docs_target +SUBDIRS = \ + template \ diff --git a/doc/examples/template/LICENSE b/doc/examples/template/LICENSE new file mode 100644 index 00000000..e8c3f505 --- /dev/null +++ b/doc/examples/template/LICENSE @@ -0,0 +1,503 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; 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. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/doc/examples/template/README.md b/doc/examples/template/README.md new file mode 100644 index 00000000..95980761 --- /dev/null +++ b/doc/examples/template/README.md @@ -0,0 +1,4 @@ +# template +-------------------------------- + +Description of the plugin... diff --git a/doc/examples/template/debian/changelog b/doc/examples/template/debian/changelog new file mode 100644 index 00000000..c22d3b82 --- /dev/null +++ b/doc/examples/template/debian/changelog @@ -0,0 +1,5 @@ +nymea-plugin-template (0.0.1) xenial; urgency=medium + + * Initial release. + + -- Developer Name Mon, 21 Aug 2017 10:12:00 +0000 diff --git a/doc/examples/template/debian/compat b/doc/examples/template/debian/compat new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/doc/examples/template/debian/compat @@ -0,0 +1 @@ +9 diff --git a/doc/examples/template/debian/control b/doc/examples/template/debian/control new file mode 100644 index 00000000..83e77989 --- /dev/null +++ b/doc/examples/template/debian/control @@ -0,0 +1,22 @@ +Source: nymea-plugin-template +Section: utils +Priority: options +Maintainer: Developer Name +Build-depends: debhelper (>= 9.0.0), + libnymea1-dev, +Standards-Version: 3.9.3 + +Package: nymea-plugin-template +Architecture: any +Multi-Arch: same +Section: libs +Depends: ${shlibs:Depends}, + ${misc:Depends}, +Description: nymea.io plugin for template + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for template diff --git a/doc/examples/template/debian/copyright b/doc/examples/template/debian/copyright new file mode 100644 index 00000000..16102e82 --- /dev/null +++ b/doc/examples/template/debian/copyright @@ -0,0 +1,14 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ${ProjectName} +Upstream-Contact: Developer Name +Copyright: 2018, Developer Name + + +License: LGPL-2.1 + On Debian systems, the complete text of the GNU Lesser General + Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. + +Files: * +License: LGPL-2.1 +Copyright: Developer Name + diff --git a/doc/examples/template/debian/nymea-plugin-template.install.in b/doc/examples/template/debian/nymea-plugin-template.install.in new file mode 100644 index 00000000..5fb557ab --- /dev/null +++ b/doc/examples/template/debian/nymea-plugin-template.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_deviceplugintemplate.so diff --git a/doc/examples/template/debian/rules b/doc/examples/template/debian/rules new file mode 100644 index 00000000..2c4801ea --- /dev/null +++ b/doc/examples/template/debian/rules @@ -0,0 +1,25 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +export DH_VERBOSE=1 +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) + +PREPROCESS_FILES := $(wildcard debian/*.in) + +$(PREPROCESS_FILES:.in=): %: %.in + sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@ + +%: + dh $@ --parallel + +override_dh_install: $(PREPROCESS_FILES:.in=) + make -j9 install DESTDIR=debian/tmp AM_UPDATE_INFO_DIR=no INSTALL_ROOT=debian/tmp + dh_install --fail-missing + +override_dh_auto_build: + dh_auto_build + make lrelease + +override_dh_auto_clean: + dh_auto_clean + rm -rf $(PREPROCESS_FILES:.in=) diff --git a/doc/examples/template/debian/source/format b/doc/examples/template/debian/source/format new file mode 100644 index 00000000..89ae9db8 --- /dev/null +++ b/doc/examples/template/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/doc/examples/template/deviceplugintemplate.cpp b/doc/examples/template/deviceplugintemplate.cpp new file mode 100644 index 00000000..af090665 --- /dev/null +++ b/doc/examples/template/deviceplugintemplate.cpp @@ -0,0 +1,67 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Developer Name * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "plugininfo.h" +#include "deviceplugintemplate.h" + +DevicePluginExample::DevicePluginExample() +{ + +} + +void DevicePluginExample::init() +{ + // Initialize/create objects +} + +void DevicePluginExample::startMonitoringAutoDevices() +{ + // Start seaching for devices which can be discovered and added automatically +} + +void DevicePluginExample::postSetupDevice(Device *device) +{ + qCDebug(dcTemplate()) << "Post setup device" << device->name() << device->params(); + + // This method will be called once the setup for device is finished +} + +void DevicePluginExample::deviceRemoved(Device *device) +{ + qCDebug(dcTemplate()) << "Remove device" << device->name() << device->params(); + + // Clean up all data related to this device +} + +DeviceManager::DeviceSetupStatus DevicePluginExample::setupDevice(Device *device) +{ + qCDebug(dcTemplate()) << "Setup device" << device->name() << device->params(); + + return DeviceManager::DeviceSetupStatusSuccess; +} + +DeviceManager::DeviceError DevicePluginExample::executeAction(Device *device, const Action &action) +{ + qCDebug(dcTemplate()) << "Executing action for device" << device->name() << action.actionTypeId().toString() << action.params(); + + return DeviceManager::DeviceErrorNoError; +} diff --git a/doc/examples/template/deviceplugintemplate.h b/doc/examples/template/deviceplugintemplate.h new file mode 100644 index 00000000..172ff472 --- /dev/null +++ b/doc/examples/template/deviceplugintemplate.h @@ -0,0 +1,54 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Developer Name * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINEXAMPLE_H +#define DEVICEPLUGINEXAMPLE_H + +#include "devicemanager.h" +#include "plugin/deviceplugin.h" + +class DevicePluginExample: public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugintemplate.json") + Q_INTERFACES(DevicePlugin) + + +public: + explicit DevicePluginExample(); + + void init() override; + void startMonitoringAutoDevices() override; + void postSetupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + + DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; + +private: + +private slots: + +}; + +#endif // DEVICEPLUGINEXAMPLE_H diff --git a/doc/examples/template/deviceplugintemplate.json b/doc/examples/template/deviceplugintemplate.json new file mode 100644 index 00000000..cc68e551 --- /dev/null +++ b/doc/examples/template/deviceplugintemplate.json @@ -0,0 +1,39 @@ +{ + "name": "Template", + "displayName": "Template", + "id": "00000000-0000-0000-0000-000000000000", + "vendors": [ + { + "name": "guh", + "displayName": "nymea", + "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", + "deviceClasses": [ + { + "name": "template", + "displayName": "Example", + "id": "00000000-0000-0000-0000-000000000000", + "deviceIcon": "None", + "setupMethod": "JustAdd", + "createMethods": ["User"], + "interfaces": [ ], + "basicTags": [ ], + "paramTypes": [ + + ], + "stateTypes":[ + + ], + "actionTypes":[ + + ], + "eventTypes":[ + + ] + } + ] + } + ] +} + + + diff --git a/doc/examples/template/template.pro b/doc/examples/template/template.pro new file mode 100644 index 00000000..42afd3bc --- /dev/null +++ b/doc/examples/template/template.pro @@ -0,0 +1,14 @@ +include(/usr/include/nymea/plugin.pri) + +TARGET = $$qtLibraryTarget(nymea_deviceplugintemplate) + +message(============================================) +message("Qt version: $$[QT_VERSION]") +message("Building $$deviceplugin$${TARGET}.so") + +SOURCES += \ + deviceplugintemplate.cpp + +HEADERS += \ + deviceplugintemplate.h + diff --git a/doc/generate-api-qdoc.py b/doc/generate-api-qdoc.py index d18f4577..dda33024 100755 --- a/doc/generate-api-qdoc.py +++ b/doc/generate-api-qdoc.py @@ -167,6 +167,7 @@ def writeDocumentationContent(apiVersion, apiJson): printInfo('--> Write API documentation content') printInfo('--> API version: \"%s\"' % (version)) + writeToFile('/*!') writeToFile('In the following section you can find a detaild description of the current API version %s.' % apiVersion) writeToFile('\list') @@ -190,6 +191,7 @@ def writeDocumentationContent(apiVersion, apiJson): writeToFile("\section1 Full introspect") writeCodeSection(apiJson) + writeToFile('*/') ########################################################################### diff --git a/doc/getting-started-plugins.qdoc b/doc/getting-started-plugins.qdoc index 1ecd8d07..95e386eb 100644 --- a/doc/getting-started-plugins.qdoc +++ b/doc/getting-started-plugins.qdoc @@ -3,18 +3,29 @@ \title Getting Started \brief Understanding the basic concept of nymea plugins - \b{\unicode{0x2192} \underline{\l{https://www.youtube.com/watch?v=B8oqItKCcgU}{Video for this tutorial}}} - - Plugins in nymea are used to exand the functionalitys and capabilitys of the nymea server. A plugin is basically a shared library, which will be loaded dynamically from the nymea server during the start up process. Each plugin has a \e name, a \e uuid and a list of supported \e vendors which will be visible in the system once the plugin is loaded. Each of thouse \l{Vendor}{Vendors} contains a list of supported \l{DeviceClass}{DeviceClasses}. A \l{DeviceClass} describes how the supported \l{Device} looks like, how it will be created (\l{DeviceClass::CreateMethod}{CreateMethod}), how the setup (\l{DeviceClass::SetupMethod}{SetupMethod}) looks like and what you can do with the \l{Device}. + Plugins in nymea are used to expand the functionalitys and capabilitys of the nymea server. A plugin is basically a shared library, + which will be loaded dynamically from the nymea server during the start up process. Each plugin has a \e name, a \e uuid and a + list of supported \e vendors which will be visible in the system once the plugin is loaded. Each of thouse \l{Vendor}{Vendors} contains + a list of supported \l{DeviceClass}{DeviceClasses}. A \l{DeviceClass} describes how the supported \l{Device} looks like, how it will be + created (\l{DeviceClass::CreateMethod}{CreateMethod}), how the setup (\l{DeviceClass::SetupMethod}{SetupMethod}) looks like and what you + can do with the \l{Device}. \section1 Devices A device in nymea can represent a real device, a gateway or even a service like weather. When you want to represent you own device / service in nymea, you should try to abstract that device and think in terms like: \list - \li \l{ParamType}{ParamTypes} \unicode{0x2192} A \l{Device} can have \l{Param}{Params}, which will be needed to set up the device (like IP addresses or device configurations) and give information for the user like name or location. The \l{ParamType} represents the description of an actual \l{Param}. - \li \l{StateType}{StateTypes} \unicode{0x2192} A \l{Device} can have \l{State}{States}, which basically represent a value of a \l{Device} like \e {current temperature} or \e ON/OFF. The \l{StateType} represents the description of an actual \l{State}. - \li \l{EventType}{EventTypes} \unicode{0x2192} A \l{Device} can emit \l{Event}{Events}, which basically represent a signal. An example of an \l{Event} could be: \e {Button pressed}. An \l{Event} can have \l{Param}{Params} to give the possibility to pass information with the signal. The \l{EventType} represents the description of an actual \l{Event}. - \li \l{ActionType}{ActionTypes} \unicode{0x2192} A \l{Device} can execute \l{Action}{Actions}, which represent basically a method for the \l{Device} which the user can execute. An example of an \l{Action} could be: \e {Set temperature}. An \l{Action} can have \l{Param}{Params} to give the possibility to parameterize the action. The \l{ActionType} represents the description of an actual \l{Action}. + \li \l{ParamType}{ParamTypes} \unicode{0x2192} A \l{Device} can have \l{Param}{Params}, which will be needed to set up the device + (like IP addresses or device configurations) and give information for the user like name or location. The \l{ParamType} represents + the description of an actual \l{Param}. + \li \l{StateType}{StateTypes} \unicode{0x2192} A \l{Device} can have \l{State}{States}, which basically represent a value of a \l{Device} + like \e {current temperature} or \e ON/OFF. The \l{StateType} represents the description of an actual \l{State}. + \li \l{EventType}{EventTypes} \unicode{0x2192} A \l{Device} can emit \l{Event}{Events}, which basically represent a signal. + An example of an \l{Event} could be: \e {Button pressed}. An \l{Event} can have \l{Param}{Params} to give the possibility to pass + information with the signal. The \l{EventType} represents the description of an actual \l{Event}. + \li \l{ActionType}{ActionTypes} \unicode{0x2192} A \l{Device} can execute \l{Action}{Actions}, which represent basically + a method for the \l{Device} which the user can execute. An example of an \l{Action} could be: \e {Set temperature}. + An \l{Action} can have \l{Param}{Params} to give the possibility to parameterize the action. The \l{ActionType} represents + the description of an actual \l{Action}. \endlist The \l{DeviceClass} represents the description of an actual \l{Device}. @@ -23,197 +34,8 @@ The \e libnymea provides a list of \l{Hardware Resources}{HardwareResources}, which can be used in every plugin. When sou start writing a plugin, you need to know which resource you will need. Each resource provides it's own interface for a \l{DevicePlugin}. In the plugin you don't have to take care about the resource. \section1 Getting started with a plugin - In order to show how a plugin ist structured here is an example of the most minimalistic device plugin possible for the nymea system. - - For an easier start we provide a set of plugin templates which can be used for your own plugin and to have a basic for the tutorials described in this documentation. You can get the templates with following command: - - \code - $ git clone https://github.com/guh/plugin-templates.git - \endcode - - This will create the \tt plugin-templates folder containing all templates and examples you will need to write your own plugin. Let's start with the smallest, simplest plugin. - - \section2 Basic structure - - The name of the plugin should be clear and inform about the content in one word. In this first minimalistic example the \b is \e "minimal". - - The basic structure of the \e minimal plugin looks like this: - - \code - $ cd plugin-templates - $ ls -l minimal/ - - devicepluginminimal.cpp - devicepluginminimal.h - devicepluginminimal.json - minimal.pro - plugins.pri - \endcode - - \section3 minimal.pro - The \tt minimal.pro file is the project file of the plugin, which can be openend with the \tt{Qt Creator}. The name of this file should be the same as the folder name of the project. In this example the name would be \e "minimal". - - \code - include(plugins.pri) - - TARGET = $$qtLibraryTarget(nymea_devicepluginminimal) - - message("Building $$deviceplugin$${TARGET}.so") - - SOURCES += \ - devicepluginminimal.cpp \ - - HEADERS += \ - devicepluginminimal.h \ - \endcode - - The \b TARGET parameter definens the name of the resulting plugin lib file and should should have following name structure: - - \tt {nymea_deviceplugin\b} - - In this example the pluginname is \e minimal, which means the lib file name will be \e nymea_devicepluginminimal.so. You can check the name of the plugin in the "Project Messages" (\tt{alt + 6} in Qt Creator). - - The \b SOURCES and \b HEADERS variables define the \tt .cpp and \tt .h files of your plugin like in any other Qt project. - \section3 plugins.pri - The \tt plugins.pri file contains all relevant definitions and configuration to build a plugin. Each plugin must contain this file and \underline{should not} be changed. In this file the precompiler \b nymea-generateplugininfo will be called. - \code - TEMPLATE = lib - CONFIG += plugin - QT += network - - QMAKE_CXXFLAGS += -Werror -std=c++11 - QMAKE_LFLAGS += -std=c++11 - - INCLUDEPATH += /usr/include/nymea - LIBS += -lnymea - - infofile.output = plugininfo.h - infofile.commands = /usr/bin/nymea-generateplugininfo ${QMAKE_FILE_NAME} ${QMAKE_FILE_OUT} - infofile.depends = /usr/bin/nymea-generateplugininfo - infofile.CONFIG = no_link - JSONFILES = deviceplugin"$$TARGET".json - infofile.input = JSONFILES - - QMAKE_EXTRA_COMPILERS += infofile - - target.path = /usr/lib/nymea/plugins/ - INSTALLS += target - \endcode - - - If you need an extra Qt module i.e. \tt serialport please add it in the \tt .pro file an \underline{not} in the \tt plugin.pri: - \note Please make sure you have installed the corresponding development libs i.e. \b{\tt{sudo apt-get install libqt5serialport5-dev}} - \code - QT += serialport - \endcode - - \section3 devicepluginminimal.json - The properties of a plugin will be definend with in the \tt JSON file containing all needed information for nymea to load it. The name convention fot the json file is: - - \tt {deviceplugin\b{}.json} - - This file must have a clear, definend structure and looking like this: - - \note For more details about the structure, values and objects please take a look at the \l{The Plugin JSON File} documentation. - - \code - { - "name": "Minimal plugin", - "idName": "Minimal", - "id": "6878754a-f27d-4007-a4e5-b030b55853f5", - "vendors": [ - { - "name": "Minimal vendor", - "idName": "minimal", - "id": "3897e82e-7c48-4591-9a2f-0f56c55a96a4", - "deviceClasses": [ - { - "deviceClassId": "7014e5f1-5b04-407a-a819-bbebd11fa372", - "idName": "minimal", - "name": "Minimal device", - "createMethods": ["user"], - "basicTags": [ - "Device" - ], - "paramTypes": [ - { - "name": "name", - "type": "QString", - "defaultValue": "Minimal device" - } - ] - } - ] - } - ] - } - \endcode - - In this minimal example of a device plugin we have one \l{Vendor} ("Minimal vendor") with the VendorId \tt {3897e82e-7c48-4591-9a2f-0f56c55a96a4}, which contains one \l{DeviceClass} with the name "Minimal device". The \l{DeviceClass} has one QString \l{Param} called \e name. - - \section3 devicepluginminimal.h - The main header file of the plugin. The naming convention is: - - \tt {deviceplugin\b.h} - - In this file you can find the main class of the plugin: \e DevicePluginMinimal. As you can see the \e DevicePluginMinimal inherits from the class \l{DevicePlugin}. You can check out the \l{DevicePlugin} class description to find how a \l{DevicePlugin} looks like. - - \code - #ifndef DEVICEPLUGINMINIMAL_H - #define DEVICEPLUGINMINIMAL_H - - #include "plugin/deviceplugin.h" - #include "devicemanager.h" - - class DevicePluginMinimal : public DevicePlugin - { - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginminimal.json") - Q_INTERFACES(DevicePlugin) - - public: - explicit DevicePluginMinimal(); - - DeviceManager::HardwareResources requiredHardware() const override; - DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - }; - - #endif // DEVICEPLUGINBOBLIGHT_H - \endcode - - As you can see this class has two methods which override the corresponding method in the \l{DevicePlugin} class. These two methods are pure virtual, which meas you \underline{must} implement them. - - \section3 devicepluginminimal.cpp - The implementation of the \l{DevicePlugin}. The naming convention is: - - \tt {deviceplugin\b.cpp} - - \code - #include "devicepluginminimal.h" - #include "plugininfo.h" - - DevicePluginMinimal::DevicePluginMinimal() - { - } - - DeviceManager::HardwareResources DevicePluginMinimal::requiredHardware() const - { - return DeviceManager::HardwareResourceNone; - } - - DeviceManager::DeviceSetupStatus DevicePluginMinimal::setupDevice(Device *device) - { - Q_UNUSED(device) - qCDebug(dcMinimal) << "setup device" << device->name() << device->id(); - - return DeviceManager::DeviceSetupStatusSuccess; - } - \endcode - - Now you can start with \l{Tutorial 1 - The "Minimal" plugin}. */ diff --git a/doc/jsonrpc-api.qdoc b/doc/jsonrpc-api.qdoc index a95256fe..8f29c675 100644 --- a/doc/jsonrpc-api.qdoc +++ b/doc/jsonrpc-api.qdoc @@ -1,4 +1,5 @@ -In following section you can find a detaild description of the current API version 1.4. +/*! +In the following section you can find a detaild description of the current API version 1.4. \list \li \l{Types} \li \l{Methods} @@ -3764,3 +3765,4 @@ Emitted whenever a Rule was removed. } } \endcode +*/ diff --git a/doc/jsonrpc.qdoc b/doc/jsonrpc.qdoc index 93bff9c1..ffba9356 100644 --- a/doc/jsonrpc.qdoc +++ b/doc/jsonrpc.qdoc @@ -201,12 +201,12 @@ In order to send a request to the server, the client has to send an API message according to the format descibed \l{Message format}{here}. The JSON content should always be sent as a compack JSON Object (without spaces, tabs and new line characters within the object \tt{{...}}). - At the end of this compact JSON string the payload must be terminated with the \tt{\n} character. This makes sure, that the parsing of the + At the end of this compact JSON string the payload must be terminated with the \tt{\\n} character. This makes sure, that the parsing of the message is easier and a single message can be split in multiple cunckes during the transport. The client parsing should work the same way. In order to demonstrate this with an example, the \l{JSONRPC.Hello} request could look like this: - \note Send following content as compact JSON ending with \tt{\n}: + \note Send following content as compact JSON ending with \tt{\\n}: \code {"id":122,"method":"JSONRPC.Hello"} @@ -218,7 +218,7 @@ to our request. The server will return a response for this request. The response will have the same \tt id as your request. The json content of the response will - also be a compact JSON string ending with the \tt{\n} character. + also be a compact JSON string ending with the \tt{\\n} character. \code {"id":122,"params":{"authenticationRequired":false,"id":0,"initialSetupRequired":false,"language":"de_DE","name":"My nymea","protocol version":"1.2","pushButtonAuthAvailable":false,"server":"nymea","uuid":"{42842b0f-a7bb-4a94-b624-a55f31c5603e}","version":"0.8.3"},"status":"success"} diff --git a/doc/libnymea.qdoc b/doc/libnymea.qdoc index 31743a9c..44827c63 100644 --- a/doc/libnymea.qdoc +++ b/doc/libnymea.qdoc @@ -15,5 +15,3 @@ \annotatedlist coap-group */ - - diff --git a/doc/nymea.qdoc b/doc/nymea.qdoc index 8708f932..400a30b2 100644 --- a/doc/nymea.qdoc +++ b/doc/nymea.qdoc @@ -36,7 +36,7 @@ \li \l{Getting started} \li \l{The plugin JSON File} \li \l{CreateMethods and SetupMethods} - \li \l{Tutorials} + \li \l{Plugin Tutorials} \endlist \section1 Client developers diff --git a/doc/plugin-tutotrials.qdoc b/doc/plugin-tutotrials.qdoc new file mode 100644 index 00000000..bace0b00 --- /dev/null +++ b/doc/plugin-tutotrials.qdoc @@ -0,0 +1,7 @@ +/*! + \page plugin-tutorials.html + \title Plugin tutorials + + \annotatedlist tutorials +*/ + diff --git a/doc/plugin-wizard.qdoc b/doc/plugin-wizard.qdoc new file mode 100644 index 00000000..d24d9af8 --- /dev/null +++ b/doc/plugin-wizard.qdoc @@ -0,0 +1,14 @@ +/*! + \example template + \title Plugin wizard + \ingroup tutorials + \brief Explanation of the Qt Creator plugin wizard template + + + This tutorial shows you how to start a new plugin project using the nymea-qtcreator wizard. You can find the source code of the + qt-creator wizard on our \l{https://github.com/guh/nymea-qtcreator-wizards}{github page}. + + + + +*/ diff --git a/doc/setup-environment.qdoc b/doc/setup-environment.qdoc index 4b2f9358..54c9fbea 100644 --- a/doc/setup-environment.qdoc +++ b/doc/setup-environment.qdoc @@ -1,13 +1,14 @@ /*! \page build-environment.html \title Set up the build environment - \brief This tutorial shows you how to set up the build environment for developing nymea. + \brief This tutorial shows you how to set up the build environment for developing with nymea. - \b{\unicode{0x2192} \underline{\l{https://www.youtube.com/watch?v=7a_k0C1Ib1A}{Video for this tutorial}}} - - Assuming you are working on an Ubuntu system here are the steps how to set up the build environment. Basically you can choose your preferred SDK but all tutorials are based on the Qt Creator and we reccommend to use that one. You can also use the Ubuntu SDK, which is basically a modified Qt Creator. + Assuming you are working on an Ubuntu system here are the steps how to set up the build environment. Basically you + can choose your preferred SDK but all tutorials are based on the Qt Creator and we recommend to use that one. - \note Please take care that you are using the Qt version from the system for building. The nymea server will always be built with the official Qt version for the appropriate system version. The plugin \underline{must} have the same version like the nymea server. + \note Please take care that you are using the Qt version from the system for building. The nymea server will always be + built with the official Qt version for the appropriate system version. The plugin \underline{must} have the same + version like the nymea server. \section2 Install Qt In the first step you need to install the Qt libraries: @@ -20,27 +21,8 @@ You can find a good instructions how to install the nymea repository on your system here: - \b {\unicode{0x2192}} \l{https://github.com/guh/nymea/wiki/Install}{nymea install wiki} + \b {\unicode{0x2192}} \l{https://nymea.io/en/wiki/nymea/master/install}{nymea install wiki} - For example, if you are working on Ubuntu 15.04 Vivid, you can create a source list file and add the nymea repository like this: - - \code - $ sudo nano /etc/apt/sources.list.d/nymea.list - \endcode - - Copy following 3 lines in the \tt /etc/apt/sources.list.d/nymea.list file, save and close it - \code - ## nymea repo - deb http://repo.nymea.io xenial main - deb-src http://repo.nymea.io xenial main - \endcode - - Now you need to add the public key of the \e nymea-repository to your key list with following command: - - \code - $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key 6B9376B0 - \endcode - Update your package lists: \code @@ -50,17 +32,9 @@ Now you are ready to install the nymea packages: \code - $ sudo apt-get install nymea nymea-webinterface nymea-cli nymea-doc libnymea1-dev + $ sudo apt-get install nymea nymea-cli nymea-doc libnymea1-dev nymea-qtcreator-wizards \endcode - - \list - \li \underline{\e nymea} \unicode{0x2192} the \tt nymea package is a meta package and will install the \tt nymead, \tt libnymea1 and \tt nymea-plugins package so you can start the nymea daemon. - \li \underline{\e nymea-webinterface} \unicode{0x2192} the \tt nymea-webinterface package will install the webinterface for nymea, which is accessable on \l{http://localhost:3333}. - \li \underline{\e nymea-cli} \unicode{0x2192} the \tt nymea-cli package will install the command line interface for the nymea JSON-RPC API. You can find more information \l{https://github.com/guh/nymea/wiki/nymea-cli}{here}. - \li \underline{\e nymea-doc} \unicode{0x2192} the \tt nymea-doc package will install the offline documentation on your system (this homepage). You can access the documentation in your brwoser with \l{file:///usr/share/doc/nymea/html/index.html}{file:///usr/share/doc/nymea/html/index.html}. - \li \underline{\e libnymea1-dev} \unicode{0x2192} the \tt libnymea1-dev package brings all development files of nymea (header files and lib) which will be needed to write a plugin. - \endlist - + Once you have successfully installed everything you are ready for \l{Getting started}. */ diff --git a/doc/tutorial1.qdoc b/doc/tutorial1.qdoc deleted file mode 100644 index 6dc0420d..00000000 --- a/doc/tutorial1.qdoc +++ /dev/null @@ -1,145 +0,0 @@ -/*! - \page tutorial1.html - \title Tutorial 1 - The "Minimal" plugin - \brief This tutorial shows you how to open, edit, build and load the first plugin. - \ingroup tutorials - \contentspage {Tutorials} - - \section1 Topics - This first tutorial shows you how to: - \list - \li \unicode{0x25B6} Open and edit the project - \li \unicode{0x25B6} Build and load the first plugin. - \endlist - - \section1 Open the project - Assuming you already have downloaded the \l{https://github.com/guh/nymea-plugin-template}{nymea-plugin-template} you can open the \tt {plugin-template \unicode{0x2192} minimal \unicode{0x2192} minimal.pro} file with the Qt Creator. - - \image minimal-project-open.png "Qt Creator" - - Qt will create next to the \tt {plugin-templates \unicode{0x2192} minimal \unicode{0x2192}} folder the build directory (shadow build). - - \section1 Build the plugin - In order to compile your first plugin you can press the \b "Build" button in the lower left corner of the \e {Qt Creator} window. You can follow the build process in the \e {Compile Output} window (\tt alt + \tt 4). - - The resulting build directory should look like this: - - \code - ls -l build-minimal-Desktop-Debug/ - - devicepluginminimal.o - extern-plugininfo.h - libnymea_devicepluginminimal.so - Makefile - moc_devicepluginminimal.cpp - moc_devicepluginminimal.o - plugininfo.h - \endcode - - As you can see there are two new header files: the \tt plugininfo.h and \tt extern-plugininfo.h . Thouse files were generated by the \tt {\b nymea-generateplugininfo} and contain the uuid definitions of from the \tt devicepluginminimal.json file. You can find out more about thouse files in \l{The plugin JSON File} documentation. - - The \b {\tt libnymea_devicepluginminimal.so} file is our fresh compiled deviceplugin. - - \section1 Install the plugin - - I you have installed nymea using the repository (see \l{Set up the build environment}) you will find the installed plugins here: - - \code - /usr/lib/nymea/plugins/ - \endcode - - This is the directory where we have to install the \b {\tt libnymea_devicepluginminimal.so} file. You have two possibilities to install the new plugin. For bouth methods you need \tt root permissions: - - \list 1 - \li Install using \b {\tt make}: - \code - $ cd build-minimal-Desktop-Debug/ - $ sudo make install - install -m 755 -p "libnymea_devicepluginminimal.so" "/usr/lib/nymea/plugins/libnymea_devicepluginminimal.so" - \endcode - \li Copy the file manually: - \code - $ cd build-minimal-Desktop-Debug/ - $ sudo cp libnymea_devicepluginminimal.so /usr/lib/nymea/plugins/ - \endcode - \endlist - - Once you have installed the new plugin you can test it. - - \section1 Test the plugin - - If order to test the new plugin we need to restart the nymea daemon. Please make shore there is only one instance of nymea running on your system, otherwise one of the daemons will colide with the ports of the other one. Once you have installed the \b {\tt libnymea_devicepluginminimal.so} file you need a clean start of the nymea daemon. - - \code - $ nymead -n - \endcode - - If you want to see the debug output of the \b {\tt libnymea_devicepluginminimal.so} you can start \tt nymead with the parameter \tt -d : - - \code - $ nymead -n -d Minimal - \endcode - - With this command the debug category will be enabled for this plugin. By default the debug categorie of a plugin is disabled, to keep the STDOUT clean and readable. With the \tt -d parameter of \b {\tt nymead} you can specify which categorie you want to see. The categorie name will be defined in the \tt idName parameter of the plugin object in \l{The Plugin JSON file}. - - Now you can use any nymea client application to examin the new plugin and device. Since the application \b {\tt nymea-cli} (nymea command line interface) was developed for developers, I will show you how this works with the \b {\tt nymea-cli}. - - \note You can open two terminal tabs. In the first one you can start \tt nymead with the \tt -n parameter to see the debug output. In the second one you can start \b {\tt nymea-cli} to interact with the server and test your plugin. You can find the documentation for \b {\tt nymea-cli} \l{https://github.com/guh/nymea/wiki/guh-cli}{here}. - - - \section2 nymea-cli - - The nymea command line interface \b {\tt nymea-cli} is an admin tool for testing \l{Plugins}, the \l{JSON-RPC API} and core functionalities of nymea. It communicates with the nymea daemon using the \l{JSON-RPC API} over the \l{nymeaserver::TcpServer}{TcpServer}. - - \code - $ nymea-cli - \endcode - - \section3 Add a new device - - Here is an example how to \e {Add a new device} with \b {\tt nymea-cli}. This steps should give you a feeling how the setup process works from the client to the method in the \l{DevicePlugin}. - - \list 1 - \li Open nymea-cli and enter the \b "Devices" menue: - \image nymea-cli.png "nymea-cli" - \li Select the \b {"Add a new device"} menue: - \image nymea-cli_devices_add-device.png "Add a new device" - \li Select the \b {"Minimal vendor"} from the \l{Vendor}{Vendors} list. Take a look at \l{The Plugin JSON file} \unicode{0x2192} \l{The Vendor definition} to see where this will be definend. - \image nymea-cli_devices_add-device_minimal-1.png "Select vendor" - \li Select the \b {"Minimal device"} from the list of supported devices for this \l{Vendor}. Take a look at \l{The Plugin JSON file} \unicode{0x2192} \l{The DeviceClass definition} to see where this will be definend. - \image nymea-cli_devices_add-device_minimal-2.png "Select device" - \li Now enter the \e value for the \l{Param} \e "name" of the \l{DeviceClass} \e {"Minimal device"}. Take a look at \l{The Plugin JSON file} \unicode{0x2192} \l{The ParamType definition} to see where this will be definend. This parameter will be used to set up the new \l{Device}. Once you entered the \e name for the new \l {Device} and pressed \tt enter, the \l{DeviceManager} will call the \l{DevicePlugin::setupDevice()} in the \tt devicepluginminimal.cpp. - - \image nymea-cli_devices_add-device_minimal-3.png "Set the parameter \"name\"" - - A new \l{Device} with a new \l{DeviceId} will be created and the passed to the \l{DevicePlugin::setupDevice()} in your plugin. - You can see here the implementation of the code: - \code - DeviceManager::DeviceSetupStatus DevicePluginMinimal::setupDevice(Device *device) - { - Q_UNUSED(device) - qCDebug(dcMinimal) << "Hello word! Setting up a new device:" << device->name(); - qCDebug(dcMinimal) << "The new device has the DeviceId" << device->id().toString(); - qCDebug(dcMinimal) << device->params(); - - return DeviceManager::DeviceSetupStatusSuccess; - } - \endcode - - If you started \b {\tt nymead} with the parameters \b {\tt{-n -d Minimal}} you will see following debug output: - - \code - ... - - Connection: Tcp server: new client connected: "127.0.0.1" - Minimal: Hello word! Setting up a new device: "Minimal device" - Minimal: The new device has the DeviceId "{b8d1f5a3-e892-4995-94b1-fa9aef662db2}" - Minimal: ParamList (count:1) - 0: Param(Name: "name", Value:QVariant(QString, "Name of minimal device") ) - - DeviceManager: Device setup complete. - \endcode - \endlist - - Now you can take a look at \l{Tutorial 2 - The "Buttons" plugin}. -*/ diff --git a/doc/tutorial2.qdoc b/doc/tutorial2.qdoc deleted file mode 100644 index cadc5344..00000000 --- a/doc/tutorial2.qdoc +++ /dev/null @@ -1,482 +0,0 @@ -/*! - \page tutorial2.html - \title Tutorial 2 - The "Buttons" plugin - \brief This plugin demonstrates the usage of events and actions. - \ingroup tutorials - - - In the second tutorial we make our own first plugin with the name \b {"Buttons"}. We will use this name for the naming concentions of the filenames. - - \section1 Topics - This tutorial will show you how to: - \list - \li \unicode{0x25B6} Start with a new \l{DevicePlugin}{Plugin} - \li \unicode{0x25B6} Implement an \l{Action} - \li \unicode{0x25B6} Implement an \l{Event} - \endlist - - In order to getting started with the new \b {"Button"} plugin we use the \b {"Minimal"} plugin as template and start from there. Make a copy of the minimal folder and name the new folder \b buttons-diy. In this case \b{buttons-diy} because the folder \b buttons already exits from the \tt plugin-template repository. - - \section1 Create the basic structure - \code - $cp -rv minimal/ buttons-diy/ - - ‘minimal/’ -> ‘buttons-diy/’ - ‘minimal/plugins.pri’ -> ‘buttons-diy/plugins.pri’ - ‘minimal/minimal.pro’ -> ‘buttons-diy/minimal.pro’ - ‘minimal/devicepluginminimal.json’ -> ‘buttons-diy/devicepluginminimal.json’ - ‘minimal/minimal.pro.user’ -> ‘buttons-diy/minimal.pro.user’ - ‘minimal/devicepluginminimal.h’ -> ‘buttons-diy/devicepluginminimal.h’ - ‘minimal/devicepluginminimal.cpp’ -> ‘buttons-diy/devicepluginminimal.cpp’ - \endcode - - \note Delete the \tt minimal.pro.user file if there is any. - - Now we can rename the files using the plugin name convention: - \code - $ cd buttons-diy/ - - $ mv minimal.pro buttons.pro - $ mv devicepluginminimal.h devicepluginbuttons.h - $ mv devicepluginminimal.cpp devicepluginbuttons.cpp - $ mv devicepluginminimal.json devicepluginbuttons.json - \endcode - - \section2 Change the \tt buttons.pro - Open the \tt buttons.pro file with the \e {Qt Creator} and open that file in the editor: - - \code - include(plugins.pri) - - TARGET = $$qtLibraryTarget(nymea_devicepluginminimal) - - message("Building $$deviceplugin$${TARGET}.so") - - SOURCES += \ - devicepluginminimal.cpp \ - - HEADERS += \ - devicepluginminimal.h \ - - \endcode - - \list 1 - \li Change the \tt TARGET name form \tt nymea_devicepluginminimal \unicode{0x2192} \tt nymea_devicepluginbuttons - \li Change the SOURCES file from \tt devicepluginminimal.cpp \unicode{0x2192} \tt devicepluginbuttons.cpp - \li Change the HEADERS file from \tt devicepluginminimal.h \unicode{0x2192} \tt devicepluginbuttons.h - \endlist - - Your file sould look now like this: - \code - include(plugins.pri) - - TARGET = $$qtLibraryTarget(nymea_devicepluginbuttons) - - message("Building $$deviceplugin$${TARGET}.so") - - SOURCES += \ - devicepluginbuttons.cpp \ - - HEADERS += \ - devicepluginbuttons.h \ - \endcode - - If you save the file, the header and source file should appear in the project structure of the \e {Qt Creator}. - - \section2 Change the \tt devicepluginbuttons.h - Open the \tt devicepluginbuttons.h file. - - \code - #ifndef DEVICEPLUGINMINIMAL_H - #define DEVICEPLUGINMINIMAL_H - - #include "plugin/deviceplugin.h" - #include "devicemanager.h" - - class DevicePluginMinimal : public DevicePlugin - { - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginminimal.json") - Q_INTERFACES(DevicePlugin) - - public: - explicit DevicePluginMinimal(); - - DeviceManager::HardwareResources requiredHardware() const override; - DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - }; - - #endif // DEVICEPLUGINMINIMAL_H - \endcode - - \list 1 - \li Change the \tt {#ifndef}, \tt {#define} and \tt #define name from \tt DEVICEPLUGINMINIMAL_H \unicode{0x2192} \tt DEVICEPLUGINBUTTONS_H - \li Change the class name form \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginButtons - \li Change in the \tt Q_PLUGIN_METADATA line the \tt FILE parameter from \tt "devicepluginminimal.json" \unicode{0x2192} \tt "devicepluginbuttons.json" to set \l{The Plugin JSON file}. - \li Change the constructor name from \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginButtons - \endlist - - Your file sould look now like this: - \code - #ifndef DEVICEPLUGINBUTTONS_H - #define DEVICEPLUGINBUTTONS_H - - #include "plugin/deviceplugin.h" - #include "devicemanager.h" - - class DevicePluginButtons : public DevicePlugin - { - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginbuttons.json") - Q_INTERFACES(DevicePlugin) - - public: - explicit DevicePluginButtons(); - - DeviceManager::HardwareResources requiredHardware() const override; - DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - }; - - #endif // DEVICEPLUGINBUTTONS_H - \endcode - - \section2 Change the \tt devicepluginbuttons.cpp - - Open the \tt devicepluginbuttons.cpp file. - - \code - #include "devicepluginminimal.h" - #include "plugininfo.h" - - DevicePluginMinimal::DevicePluginMinimal() - { - } - - DeviceManager::HardwareResources DevicePluginMinimal::requiredHardware() const - { - return DeviceManager::HardwareResourceNone; - } - - DeviceManager::DeviceSetupStatus DevicePluginMinimal::setupDevice(Device *device) - { - Q_UNUSED(device) - qCDebug(dcMinimal) << "Hello word! Setting up a new device:" << device->name(); - qCDebug(dcMinimal) << "The new device has the DeviceId" << device->id().toString(); - qCDebug(dcMinimal) << device->params(); - - return DeviceManager::DeviceSetupStatusSuccess; - } - \endcode - - \list 1 - \li Change the \tt {#include "devicepluginminimal.h"} \unicode{0x2192} \tt {#include "devicepluginbuttons.h"} - \li Change in each method implementation the \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginButtons namespace. - \li Change each \tt {qCDebug(dcMinimal)} \unicode{0x2192} \tt {qCDebug(dcButtons)}, you will see later why. - \endlist - - Your file sould look now like this: - - \code - #include "devicepluginbuttons.h" - #include "plugininfo.h" - - DevicePluginButtons::DevicePluginButtons() - { - } - - DeviceManager::HardwareResources DevicePluginButtons::requiredHardware() const - { - return DeviceManager::HardwareResourceNone; - } - - DeviceManager::DeviceSetupStatus DevicePluginButtons::setupDevice(Device *device) - { - Q_UNUSED(device) - qCDebug(dcButtons) << "Hello word! Setting up a new device:" << device->name(); - qCDebug(dcButtons) << "The new device has the DeviceId" << device->id().toString(); - qCDebug(dcButtons) << device->params(); - - return DeviceManager::DeviceSetupStatusSuccess; - } - \endcode - - The basic structure of our new \l{DevicePlugin} is finished. You may recognize that the \tt {plugininfo.h} file does not exist yet. You have to build the plugin to generate that file. Each time you change \l{The Plugin JSON file} this file will be new generated. - - \section2 Change the \tt devicepluginbuttons.json - - Our new plugin will have the name \b {Buttons}, the corresponding logging categorie will be \tt dcButtons (defined from the \e {idName}). There will be one new \l{DeviceClass} with the name \b {Simple Button}. This \l{DeviceClass} will have one \l{EventType} and one \l{ActionType}. - - The current \tt devicepluginbuttons.json should still look like this: - \code - { - "name": "Minimal plugin", - "idName": "Minimal", - "id": "6878754a-f27d-4007-a4e5-b030b55853f5", - "vendors": [ - { - "name": "Minimal vendor", - "idName": "minimal", - "id": "3897e82e-7c48-4591-9a2f-0f56c55a96a4", - "deviceClasses": [ - { - "deviceClassId": "7014e5f1-5b04-407a-a819-bbebd11fa372", - "idName": "minimal", - "name": "Minimal device", - "createMethods": ["user"], - "paramTypes": [ - { - "name": "name", - "type": "QString", - "defaultValue": "Simple button device default name" - } - ] - } - ] - } - ] - } - \endcode - - Now we change \l{The Plugin JSON file} for our new plugin: - \list 1 - \li Set the plugin \e name to \b Buttons - \li Set the plugin \e idName to \b Buttons (used for logging category name -> \e {dcButtons}) - \li Use \b {\tt uuidgen} to create a new UUID. Replace the old plugin \e id with the new one. - \li Set the vendor \e name to \b {Button vendor} - \li Set the vendor \e idName to \b buttons (used for VendorId variable definition in the \tt plugininfo.h -> \e buttonsVendorId}) - \li Use \b {\tt uuidgen} to create a new UUID. Replace the old vendor \e id with the new one. - \li Use \b {\tt uuidgen} to create a new UUID. Replace the old DeviceClassId \e id with the new one. - \li Set the device class \e idName to \b simpleButton (used for DeviceClassId variable definition in the \tt plugininfo.h -> \e simpleButtonDeviceClassId}) - \li Set the device class \e name to \b {Simple Button} - \li The \e createMethod is still the same: \b user - \li This single \l ParamType called \b name should be in every single DeviceClass to allow the user to give a custom name to a \l{Device}. Just change the defaultValue for the name to \b {Simple button device default name}. - \endlist - - Your device should now look like this (with your own UUIDs): - \code - { - "name": "Buttons", - "idName": "Buttons", - "id": "7bfd3af5-7983-4540-9398-d14085d069f4", - "vendors": [ - { - "name": "Button vendor", - "idName": "buttons", - "id": "fd2ae067-2c3d-4332-9c4b-ee0af653bcaf", - "deviceClasses": [ - { - "deviceClassId": "73bb670b-e7a3-40da-bd6f-3260f017ec80", - "idName": "simpleButton", - "name": "Simple Button", - "createMethods": ["user"], - "paramTypes": [ - { - "name": "name", - "type": "QString", - "defaultValue": "Simple button device default name" - } - ] - } - ] - } - ] - } - \endcode - - Now the basic structure is finished and we have a new \l{DevicePlugin}, a new \l{Vendor} and a new \l{DeviceClass}. - - Now we have to add an \l{ActionType} which will called \b press and gives the user the possibility to press this button device (see \l{The ActionType definition}) - - \list 1 - \li Use \b {\tt uuidgen} to create a new UUID for this \l{ActionType}. - \li Set the \e idName of this \l{ActionType} to \b pressSimpleButton (used for ActionTypeId variable definition in the \tt plugininfo.h -> \e pressSimpleButtonActionTypeId}) - \li Set the \e name of this \l{ActionType} to \b {press the button} - \endlist - \code - "actionTypes": [ - { - "id": "64c4ced5-9a1a-4858-81dd-1b5c94dba495", - "idName": "pressSimpleButton", - "name": "press the button" - } - ] - \endcode - - Now we have to add an \l{EventType} which will be emitted when the button was pressed. - - \list 1 - \li Use \b {\tt uuidgen} to create a new UUID for this \l{EventType}. - \li Set the \e idName of this \l{EventType} to \b simpleButtonPressed (used for EventTypeId variable definition in the \tt plugininfo.h -> \e simpleButtonPressedEventTypeId}) - \li Set the \e name of this \l{EventType} to \b {button pressed} - \endlist - - \code - "eventTypes": [ - { - "id": "f9652210-9aed-4f38-8c19-2fd54f703fbe", - "idName": "simpleButtonPressed", - "name": "button pressed" - } - ] - \endcode - - \code - { - "name": "Buttons", - "idName": "Buttons", - "id": "7bfd3af5-7983-4540-9398-d14085d069f4", - "vendors": [ - { - "name": "Button vendor", - "idName": "buttons", - "id": "fd2ae067-2c3d-4332-9c4b-ee0af653bcaf", - "deviceClasses": [ - { - "deviceClassId": "73bb670b-e7a3-40da-bd6f-3260f017ec80", - "idName": "simpleButton", - "name": "Simple Button", - "createMethods": ["user"], - "paramTypes": [ - { - "name": "name", - "type": "QString", - "defaultValue": "Simple button device default name" - } - ], - "actionTypes": [ - { - "id": "64c4ced5-9a1a-4858-81dd-1b5c94dba495", - "idName": "pressSimpleButton", - "name": "press the button" - } - ], - "eventTypes": [ - { - "id": "f9652210-9aed-4f38-8c19-2fd54f703fbe", - "idName": "simpleButtonPressed", - "name": "button pressed" - } - ] - } - ] - } - ] - } - \endcode - - Rebuild the entire project to generate the new \tt {plugininfo.h}. You need to call the \underline{Rebuild all} command in the \e {Qt Creator} to take over the changes in the \tt plugininfo.h . - - If you make a syntax error in the JSON file, you will get a build error with the position of the syntax error in the JSON file. Now your definitions should be in the plugininfo.h file and ready to use in the plugin source code. - - You will see in the build output following section: - \code - /usr/bin/nymea-generateplugininfo ../buttons-diy/devicepluginbuttons.json plugininfo.h - ../buttons-diy/devicepluginbuttons.json -> plugininfo.h - --> generate plugininfo.h - PluginId for plugin "Buttons" = 7bfd3af5-7983-4540-9398-d14085d069f4 - define VendorId ButtonsVendorId = fd2ae067-2c3d-4332-9c4b-ee0af653bcaf - define DeviceClassId simpleButtonDeviceClassId = 73bb670b-e7a3-40da-bd6f-3260f017ec80 - define logging category: "dcButtons" - --> generated successfully "plugininfo.h" - --> generate extern-plugininfo.h - --> generated successfully "extern-plugininfo.h" - \endcode - - This shows you how the \tt{plugininfo.h} and \tt{extern-plugininfo.h} will be generated. As you can see the UUID definitions and the logging category were definend for the \b {Buttons} plugin. - - Once the build step is finished, you can take a look at that file (curser in line \tt {#include "plugininfo.h"} and press \tt F2) - - Your \tt plugininfo.h should now look like this (with your own UUIDs): - \code - #ifndef PLUGININFO_H - #define PLUGININFO_H - #include "typeutils.h" - #include - - // Id definitions - PluginId pluginId = PluginId("7bfd3af5-7983-4540-9398-d14085d069f4"); - VendorId buttonsVendorId = VendorId("fd2ae067-2c3d-4332-9c4b-ee0af653bcaf"); - DeviceClassId simpleButtonDeviceClassId = DeviceClassId("73bb670b-e7a3-40da-bd6f-3260f017ec80"); - ActionTypeId pressSimpleButtonActionTypeId = ActionTypeId("64c4ced5-9a1a-4858-81dd-1b5c94dba495"); - EventTypeId simpleButtonPressedEventTypeId = EventTypeId("f9652210-9aed-4f38-8c19-2fd54f703fbe"); - - // Loging category - Q_DECLARE_LOGGING_CATEGORY(dcButtons) - Q_LOGGING_CATEGORY(dcButtons, "Buttons") - - #endif // PLUGININFO_H - \endcode - - \section1 Writing the plugin - - Now we have our basic for starting to implement the new plugin. If you install the current plugin, you would already see the plugin implementation in \tt nymea-cli, but it would do nothing because we have not implemented yet the code. - - \section2 The \tt executeAction method - - Every plugin with \l{Action}{Actions} needs the \l{DevicePlugin::executeAction()} method which should be overridden in your own plugin. - - \code - DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; - \endcode - - Here is the implemented method: - \code - // Check the DeviceClassId for "Simple Button" - if (device->deviceClassId() == simpleButtonDeviceClassId ) { - - // check if this is the "press" action - if (action.actionTypeId() == pressSimpleButtonActionTypeId) { - - qCDebug(dcButtons) << "Simple button" << device->paramValue("name").toString() << "was pressed"; - - // Emit the "button pressed" event - Event event(simpleButtonPressedEventTypeId, device->id()); - emit emitEvent(event); - - return DeviceManager::DeviceErrorNoError; - } - return DeviceManager::DeviceErrorActionTypeNotFound; - } - \endcode - - When a user or the \l {nymeaserver::RuleEngine}{RuleEngine} calls the executeAction method, our plugin will first check if the \l DeviceClassId matches, then the \l ActionTypeId. If both are correct, we can emit our \l Event to show that the simple button \l Device was pressed. - - You can see in the implementation that a new \l{Event} will be generated in nymea. This is the way how you emit an Event for a device. - - \section1 Test the plugin - Rebuild the whole project to make shore all changes are registered and install the plugin (see \l{Install the plugin}{Tutorial 1 - Install the plugin}). - - \list 1 - \li Start nymea with following command: - - \code - $ nymead -n -d Buttons - \endcode - - \li Start nymea-cli and add the a new \b {"Simple Button"} devcice. Give an appropriate name like \b {Test button}. - \li Use nymea-cli to execute the \b {press the button} action: - - \tt "Devices" \unicode{0x2192} \tt {"Execute an action"} \unicode{0x2192} \tt {"Your device name (Simple Button)"} \unicode{0x2192} \tt {press the button} - - \endlist - - \code - ... - - Connection: Tcp server: new client connected: "127.0.0.1" - Buttons: Simple button "Test button" was pressed - RuleEngine: got event: Event(EventTypeId: "{f9652210-9aed-4f38-8c19-2fd54f703fbe}", - DeviceId"{967d4c50-7cc5-4114-865a-822c64a1e7ce}") "Simple Button" - QUuid ("{f9652210-9aed-4f38-8c19-2fd54f703fbe}") - - \endcode - - Now you have successfully implemented you first DeviceClass, which has an \l Action and an \l Event and can be used together with the \l {nymeaserver::RuleEngine}{RuleEngine}. - - Now you can take a look at \l{Tutorial 3 - The "Power Button" device}. - -*/ - - diff --git a/doc/tutorial3.qdoc b/doc/tutorial3.qdoc deleted file mode 100644 index db81ad93..00000000 --- a/doc/tutorial3.qdoc +++ /dev/null @@ -1,141 +0,0 @@ -/*! - \page tutorial3.html - \title Tutorial 3 - The "Power Button" device - \brief This device demonstrates the usage of states and params. - \ingroup tutorials - - - In the third tutorial we use the project from \l{Tutorial 2 - The "Buttons" plugin}{Tutorial 2} and add a new button type to the existing plugin. - - \section1 Topics - This tutorial will show you how to: - \list - \li \unicode{0x25B6} Implement a \l{State} - \li \unicode{0x25B6} Use \l{Param}{Params} in an \l{Event} - \li \unicode{0x25B6} Use \l{Param}{Params} in an \l{Action} - \endlist - - Our new "Power Button" will be able to execute an \l Action and set the \l State of the \l Device \tt true or \tt false . - - \section1 Add a new DeviceClass - - Open the \tt devicepluginbuttons.json: - - Add the new DeviceClass right behind the existing one: - - \code - ... - }, - { - "deviceClassId": "fb587886-a649-42d0-9609-8423de587685", - "idName": "powerButton", - "name": "Power Button", - "createMethods": ["user"], - "paramTypes": [ - { - "name": "name", - "type": "QString", - "defaultValue": "Power button device default name" - } - ], - "actionTypes": [ - { - "id": "1e97a057-c525-463d-a593-9b8dce16645f", - "idName": "setPowerButton", - "name": "set power", - "paramTypes": [ - { - "name": "power", - "type": "bool", - "defaultValue": false - } - ] - } - ], - "stateTypes": [ - { - "id": "9328693e-9054-47bc-b95f-ae3e42d50b8b", - "idName": "power", - "name": "power", - "type": "bool", - "defaultValue": false - } - ] - } - - ... - \endcode - - As you can see we added a \l ParamType to the \l ActionType, removed the \l EventType and added a \l StateType definition. - - When the \l Action "set power" will be executed, a \l Param named "power" will be passed with it from type \tt bool. This param holds the power status, which can be true or false and set the new \l State with the name "power". - - \section1 Writing the plugin - - Since we have a new ActionType and a new DeviceClass, we have to change the \tt {executeAction()} method in the \b {"Button"} plugin. - - \code - ... - - // Check the DeviceClassId for "Power Button" - if (device->deviceClassId() == powerButtonDeviceClassId ) { - - // check if this is the "set power" action - if (action.actionTypeId() == setPowerButtonActionTypeId) { - - // get the param value - Param powerParam = action.param("power"); - bool power = powerParam.value().toBool(); - - qCDebug(dcButtons) << "Power button" << device->paramValue("name").toString() << "set power to" << power; - - // Set the "power" state - device->setStateValue(powerStateTypeId, power); - - return DeviceManager::DeviceErrorNoError; - } - return DeviceManager::DeviceErrorActionTypeNotFound; - } - - ... - \endcode - - When a \l State value of a \l Device will be set, that will generate an \l Event in nymea, which contains a \l Param holding the new \l State value. - - \note You \underline don't have to take care about that \l Event, it will be generated automatically and will have the same uuid as \l EventTypeId like the \l StateTypeId. This makes it possible for client applications to link the \l Event to the corresponding \l State which generated the \l Event. - - \note You \underline don't have to check if the \l State value has changed or not when you set the value. i.e. if the current \l State value of the power \l State is \tt true, and the \l Action \l Param is also \tt true, this code will not generate the \l Event because the value has not changed. - - \section1 Test the plugin - Rebuild the whole project to make shore all changes are registered and install the plugin (see \l{Install the plugin}{Tutorial 1 - Install the plugin}). - - \list 1 - \li Start nymea with following command: - - \code - $ nymead -n -d Buttons - \endcode - - \li Start nymea-cli and add the a new \b {"Power Button"} \l Device. Give an appropriate name like \b {Light}. - \li Use nymea-cli to check the current power \l State, it should be \tt false (default value). - \li Use nymea-cli to execute the \b {set power} \l Action: - - \tt "Devices" \unicode{0x2192} \tt "Execute an action" \unicode{0x2192} \tt {"Your device name (Power Button)"} \unicode{0x2192} \tt {set power} \unicode{0x2192} \tt {true} - \endlist - - In the nymead stdout you should see the debug output from you plugin. - \code - ... - - Connection: Tcp server: new client connected: "127.0.0.1" - Buttons: Power button "Light" set power to true - RuleEngine: got event: Event(EventTypeId: "{9328693e-9054-47bc-b95f-ae3e42d50b8b}", - DeviceId"{2304632a-a77a-452f-b438-87e7d69e9a00}") "Power Button" - QUuid("{9328693e-9054-47bc-b95f-ae3e42d50b8b}") - \endcode - - Now you have successfully implemented you first DeviceClass, which has an parametrized \l Action and a \l State which generates an \l Event containig the new \l State value. This new DeviceClass can be used in the \l {nymeaserver::RuleEngine}{RuleEngine}. Feel free to play with the Device and the Rule engine to get a feeling how the system works. - - Now you can take a look at \l{Tutorial 4 - The alternative "Power Button"}. - -*/ diff --git a/doc/tutorial4.qdoc b/doc/tutorial4.qdoc deleted file mode 100644 index 2130e0c8..00000000 --- a/doc/tutorial4.qdoc +++ /dev/null @@ -1,122 +0,0 @@ -/*! - \page tutorial4.html - \title Tutorial 4 - The alternative "Power Button" - \brief This device demonstrates how a writable state works. - \ingroup tutorials - - \section1 Topics - This tutorial will show you how to: - \list - \li \unicode{0x25B6} Implement a writable \l{State} (which can be manipulated by an \l{Action}) - \endlist - - In the fourth tutorial you will see how a \e writable \l State works. We use the \l DeviceClass \b {"Power Button"} from the previouse \l{Tutorial 3 - The "Power Button" device}{Tutorial 3} for that and create a new one called \b {"Alternative Power Button"}. It does exactly the same like the \b {"Power Button"} except it will be created in a different way for a good reason. - - \section1 Add a new DeviceClass - Let's start again with the \tt devicepluginbuttons.json and append a new \l DeviceClass definition: - - \code - ... - - }, - { - "deviceClassId": "910b2f58-70dc-4da3-89ae-9e7393290ccb", - "idName": "alternativePowerButton", - "name": "Alternative Power Button", - "createMethods": ["user"], - "paramTypes": [ - { - "name": "name", - "type": "QString", - "defaultValue": "Alternative power button device default name" - } - ], - "stateTypes": [ - { - "id": "fa63c0b9-10e5-4280-9cc2-243bf27c05ad", - "idName": "alternativePower", - "name": "power", - "type": "bool", - "defaultValue": false, - "writable": true - } - ] - } - - ... - \endcode - - As you can see, there is only one \tt bool \l State which has the property \e {"writable"}. This property indicates, that this \l State is writable and we need an \l Action for doing that. You can find more details about this property in \l {The StateType definition} documentation. - - We learnend in the previouse tutorial that a \l State will always generate an \l Event when he changes his \e {value}. This \l Event has the same UUID as the \l State which generated the \l Event. The same thing happens with the \l Action if you make a \l State writable. The device manager defines a new \l ActionType which has the same UUID as the \l State which will be changed with the \l Action. - - \tt {\l{StateTypeId} == \l{EventTypeId} == \l{ActionTypeId}} - - This makes it possible for clients to link the \l Action to the \l State which will be changed to the \l Event which will be emitted if the \l State was changed. All the types have the same UUID and the same Param. - - \section1 Writing the plugin - The implementation in the \tt executeAction() is almost the same like in the previous tutorial execpt the UUID variables changed and a new debug output was inseted to show the UUID. - - \code - ... - - // Check the DeviceClassId for "Alternative Power Button" - if (device->deviceClassId() == alternativePowerButtonDeviceClassId) { - - // check if this is the "set power" action - if (action.actionTypeId() == alternativePowerActionTypeId) { - - // get the param value - Param powerParam = action.param("power"); - bool power = powerParam.value().toBool(); - - qCDebug(dcButtons) << "Alternative power button" << device->paramValue("name").toString() << "set power to" << power; - qCDebug(dcButtons) << "ActionTypeId :" << action.actionTypeId().toString(); - qCDebug(dcButtons) << "StateTypeId :" << alternativePowerStateTypeId.toString(); - - // Set the "power" state - device->setStateValue(alternativePowerStateTypeId, power); - - return DeviceManager::DeviceErrorNoError; - } - return DeviceManager::DeviceErrorActionTypeNotFound; - } - - ... - \endcode - - - \section1 Test the plugin - Rebuild the whole project to make shore all changes are registered and install the plugin (see \l{Install the plugin}{Tutorial 1 - Install the plugin}). - - \list 1 - \li Start nymea with following command: - - \code - $ nymead -n -d Buttons - \endcode - - \li Start nymea-cli and add the a new \b {"Alternative Power Button"} \l Device. Give an appropriate name like \b {Alternative Light}. - \li Use nymea-cli to check the current power \l State, it should be \tt false (default value). - \li Use nymea-cli to execute the \b {set power} \l Action: - - \tt "Devices" \unicode{0x2192} \tt {"Execute an action"} \unicode{0x2192} \tt {"Your device name (Alternative Power Button)"} \unicode{0x2192} \tt {set power} \unicode{0x2192} \tt {true} - \endlist - - In the nymead STDOUT you should see the debug output from you plugin. You will notice that the ActionTypeId, StateTypeId and EventTypeId are equal. - - \code - ... - - Connection: Tcp server: new client connected: "127.0.0.1" - Buttons: Alternative power button "Alternative Light" set power to true - Buttons: ActionTypeId : "{fa63c0b9-10e5-4280-9cc2-243bf27c05ad}" - Buttons: StateTypeId : "{fa63c0b9-10e5-4280-9cc2-243bf27c05ad}" - RuleEngine: got event: Event(EventTypeId: "{fa63c0b9-10e5-4280-9cc2-243bf27c05ad}", - DeviceId"{bb1c6795-2701-49e3-9529-45d87136b731}") "Alternative Power Button" - QUuid("{fa63c0b9-10e5-4280-9cc2-243bf27c05ad}") - - \endcode - - Now you can take a look at \l{Tutorial 5 - The "Network Info" plugin}. -*/ diff --git a/doc/tutorial5.qdoc b/doc/tutorial5.qdoc deleted file mode 100644 index eca2bf7a..00000000 --- a/doc/tutorial5.qdoc +++ /dev/null @@ -1,590 +0,0 @@ -/*! - \page tutorial5.html - \title Tutorial 5 - The "Network Info" plugin - \brief The plugin shows you how to use the NetworkManager and how asynchronous actions work - \ingroup tutorials - - \section1 Topics - This tutorial will show you how to: - \list - \li \unicode{0x25B6} Use the hardware resource \l{NetworkManager} - \endlist - - - In the tutorial we make a plugin with the name \b {"Network Info"}. This plugin will use the \l{NetworkManager} hardware resource to fetch the location and WAN ip of your internet connection from \l{http://ip-api.com/json}. It will have an \l Action called \e "update" which will refresh the \l{State}{States} of the \l{Device}. - -In order to get started with our new \b {"Network Info"} plugin we use the minimal plugin as template and start from there. Make a copy of the minimal folder and name the new folder \b networkinfo-diy. In this case \b{networkinfo-diy} because the folder \b networkinfo already exits from the \tt plugin-template repository. - - \section1 Create the basic structure - \code - $ cp -rv minimal/ networkinfo-diy - ‘minimal/’ -> ‘networkinfo-diy’ - ‘minimal/plugins.pri’ -> ‘networkinfo-diy/plugins.pri’ - ‘minimal/minimal.pro’ -> ‘networkinfo-diy/minimal.pro’ - ‘minimal/devicepluginminimal.json’ -> ‘networkinfo-diy/devicepluginminimal.json’ - ‘minimal/devicepluginminimal.h’ -> ‘networkinfo-diy/devicepluginminimal.h’ - ‘minimal/devicepluginminimal.cpp’ -> ‘networkinfo-diy/devicepluginminimal.cpp’ - \endcode - - \note Delete the minimal.pro.user file if there is any. - - Now we can rename the files using the plugin name convention: - \code - $ cd networkinfo-diy/ - $ mv minimal.pro networkinfo.pro - $ mv devicepluginminimal.h devicepluginnetworkinfo.h - $ mv devicepluginminimal.cpp devicepluginnetworkinfo.cpp - $ mv devicepluginminimal.json devicepluginnetworkinfo.json - \endcode - - \section2 Change the \tt networkinfo.pro - Open the \tt networkinfo.pro file with the \e {Qt Creator} and open that file in the editor: - - \code - include(plugins.pri) - - TARGET = $$qtLibraryTarget(nymea_devicepluginminimal) - - message("Building $$deviceplugin$${TARGET}.so") - - SOURCES += \ - devicepluginminimal.cpp \ - - HEADERS += \ - devicepluginminimal.h \ - - \endcode - - \list 1 - \li Change the \tt TARGET name form \tt nymea_devicepluginminimal \unicode{0x2192} \tt nymea_devicepluginnetworkinfo - \li Change the SOURCES file from \tt devicepluginminimal.cpp \unicode{0x2192} \tt devicepluginnetworkinfo.cpp - \li Change the HEADERS file from \tt devicepluginminimal.h \unicode{0x2192} \tt devicepluginnetworkinfo.h - \endlist - - Your file sould look now like this: - \code - include(plugins.pri) - - TARGET = $$qtLibraryTarget(nymea_devicepluginnetworkinfo) - - message("Building $$deviceplugin$${TARGET}.so") - - SOURCES += \ - devicepluginnetworkinfo.cpp \ - - HEADERS += \ - devicepluginnetworkinfo.h \ - \endcode - - If you save the file, the header and source file should appear in the project structure of the \e {Qt Creator}. - - \section2 Change the \tt devicepluginnetworkinfo.h - Open the \tt devicepluginnetworkinfo.h file. - - \code - #ifndef DEVICEPLUGINMINIMAL_H - #define DEVICEPLUGINMINIMAL_H - - #include "plugin/deviceplugin.h" - #include "devicemanager.h" - - class DevicePluginMinimal : public DevicePlugin - { - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginminimal.json") - Q_INTERFACES(DevicePlugin) - - public: - explicit DevicePluginMinimal(); - - DeviceManager::HardwareResources requiredHardware() const override; - DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - }; - - #endif // DEVICEPLUGINMINIMAL_H - \endcode - - \list 1 - \li Change the \tt {#ifndef}, \tt {#define} and \tt #define name from \tt DEVICEPLUGINMINIMAL_H \unicode{0x2192} \tt DEVICEPLUGINNETWORKINFO_H - \li Change the class name form \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginNetworkInfo - \li Change in the \tt Q_PLUGIN_METADATA line the \tt FILE parameter from \tt "devicepluginminimal.json" \unicode{0x2192} \tt "devicepluginnetworkinfo.json" to set \l{The Plugin JSON file}. - \li Change the constructor name from \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginNetworkInfo - \endlist - - Your file sould look now like this: - - \code - #ifndef DEVICEPLUGINNETWORKINFO_H - #define DEVICEPLUGINNETWORKINFO_H - - #include "plugin/deviceplugin.h" - #include "devicemanager.h" - - class DevicePluginNetworkInfo : public DevicePlugin - { - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginnetworkinfo.json") - Q_INTERFACES(DevicePlugin) - - public: - explicit DevicePluginNetworkInfo(); - - DeviceManager::HardwareResources requiredHardware() const override; - DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - }; - - #endif // DEVICEPLUGINNETWORKINFO_H - \endcode - - \section2 Change the \tt devicepluginnetworkinfo.cpp - - Open the \tt devicepluginnetworkinfo.h file. - - \code - #include "devicepluginminimal.h" - #include "plugininfo.h" - - DevicePluginMinimal::DevicePluginMinimal() - { - } - - DeviceManager::HardwareResources DevicePluginMinimal::requiredHardware() const - { - return DeviceManager::HardwareResourceNone; - } - - DeviceManager::DeviceSetupStatus DevicePluginMinimal::setupDevice(Device *device) - { - Q_UNUSED(device) - qCDebug(dcMinimal) << "Hello word! Setting up a new device:" << device->name(); - qCDebug(dcMinimal) << "The new device has the DeviceId" << device->id().toString(); - qCDebug(dcMinimal) << device->params(); - - return DeviceManager::DeviceSetupStatusSuccess; - } - \endcode - - \list 1 - \li Change the \tt {#include "devicepluginminimal.h"} \unicode{0x2192} \tt {#include "devicepluginnetworkinfo.h"} - \li Change in each method implementation the \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginNetworkInfo namespace. - \endlist - - Your file sould look now like this: - - \code - #include "devicepluginnetworkinfo.h" - #include "plugininfo.h" - - DevicePluginNetworkInfo::DevicePluginNetworkInfo() - { - } - - DeviceManager::HardwareResources DevicePluginNetworkInfo::requiredHardware() const - { - return DeviceManager::HardwareResourceNone; - } - - DeviceManager::DeviceSetupStatus DevicePluginNetworkInfo::setupDevice(Device *device) - { - Q_UNUSED(device) - qCDebug(dcMinimal) << "Hello word! Setting up a new device:" << device->name(); - qCDebug(dcMinimal) << "The new device has the DeviceId" << device->id().toString(); - qCDebug(dcMinimal) << device->params(); - - return DeviceManager::DeviceSetupStatusSuccess; - } - \endcode - - The basic structure of our new \l{DevicePlugin} is finished. You may recognize that the \tt {plugininfo.h} file does not exist yet. You have to build the plugin to generate that file. Each time you change \l{The Plugin JSON file} this file will be new generated. Once the build step is finished, you can take a look at that file (curser in line \tt {#include "plugininfo.h"} and press \tt F2) - - You will see in the build output following section: - \code - /usr/bin/nymea-generateplugininfo ../networkinfo-diy/devicepluginnetworkinfo.json plugininfo.h - ../networkinfo-diy/devicepluginnetworkinfo.json -> plugininfo.h - --> generate plugininfo.h - PluginId for plugin "Minimal plugin" = 6878754a-f27d-4007-a4e5-b030b55853f5 - define VendorId MinimalVendorId = 3897e82e-7c48-4591-9a2f-0f56c55a96a4 - define DeviceClassId minimalDeviceClassId = 7014e5f1-5b04-407a-a819-bbebd11fa372 - define logging category: "dcMinimal" - --> generated successfully "plugininfo.h" - --> generate extern-plugininfo.h - --> generated successfully "extern-plugininfo.h" - \endcode - - This shows you how the \tt{plugininfo.h} and \tt{extern-plugininfo.h} will be generated. As you can see the UUID definitions and the logging category will be definend for the \b {Minimal} plugin because we have not changed yet \l{The Plugin JSON file}. - - The generated \tt {plugininfo.h} file will look like this: - \code - #ifndef PLUGININFO_H - #define PLUGININFO_H - #include "typeutils.h" - #include - - // Id definitions - PluginId pluginId = PluginId("6878754a-f27d-4007-a4e5-b030b55853f5"); - VendorId minimalVendorId = VendorId("3897e82e-7c48-4591-9a2f-0f56c55a96a4"); - DeviceClassId minimalDeviceClassId = DeviceClassId("7014e5f1-5b04-407a-a819-bbebd11fa372"); - - // Loging category - Q_DECLARE_LOGGING_CATEGORY(dcMinimal) - Q_LOGGING_CATEGORY(dcMinimal, "Minimal") - - #endif // PLUGININFO_H - \endcode - - The generated \tt {extern-plugininfo.h} file will look like this: - \code - #ifndef EXTERNPLUGININFO_H - #define EXTERNPLUGININFO_H - #include "typeutils.h" - #include - - // Id definitions - extern VendorId minimalVendorId; - extern DeviceClassId minimalDeviceClassId; - - // Logging category definition - Q_DECLARE_LOGGING_CATEGORY(dcMinimal) - - #endif // EXTERNPLUGININFO_H - \endcode - - \section2 Change the \tt devicepluginnetworkinfo.json - - Before we can write our plugin JSON file we need to know which \l{State}{States}, \l{Action}{Actions} will be available. You can take a look at the \l{http://ip-api.com/} page. For the plugin we will need thouse information in a format which we can parse i.e. JSON \unicode{0x2192} \l{http://ip-api.com/json}. - - For more details about how to write the JSON file please take a look at \l{The Plugin JSON file} documentation. - - \note As you can see in this example the \l Vendor for this \l DevicePlugin is the \e nymea. Of course you can define here a new Vendor (using \tt uuidgen to generate a new UUID). Please take a look at the existing \l{Vendor}{Vendors} and check if your \l Vendor already exists. If the \l{Vendor} exists, please copy the \e name, \e idName and \e id to make shore all \l{Device}{Devices} from one \l{Vendor} will be together in the system like in this example for \e nymea. - - Our new plugin will have the name \b {"Network Info"}, the corresponding logging categorie will be \tt dcNetworkInfo (defined from the \e {idName}). There will be one new \l{DeviceClass} with the \e name \b {Info about Network}. This \l{DeviceClass} has 6 \l{StateType}{StateTypes} and one \l{ActionType}. - - \code - { - "name": "Network Info", - "idName": "NetworkInfo", - "id": "c16852d7-f123-4dd5-983d-fc2eedb885aa", - "vendors": [ - { - "name": "nymea", - "idName": "nymea", - "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", - "deviceClasses": [ - { - "deviceClassId": "6c9d4852-cdfa-4eba-9ff2-c084d6f9d756", - "idName": "info", - "name": "Info about Network", - "createMethods": ["user"], - "paramTypes": [ - { - "name": "name", - "type": "QString", - "defaultValue": "Network Information" - } - ], - "stateTypes": [ - { - "name": "ip address", - "id": "0b4751ca-f126-4369-bfc0-f745985ae59b", - "idName": "address", - "type": "QString", - "defaultValue": "-" - }, - { - "name": "city", - "id": "8c777cf7-1a54-4b80-a8fe-141ae2334a63", - "idName": "city", - "type": "QString", - "defaultValue": "-" - }, - { - "name": "country", - "id": "69a01d64-c68f-4175-85f3-69329fd66b52", - "idName": "country", - "type": "QString", - "defaultValue": "-" - }, - { - "name": "time zone", - "id": "ab5278ce-87e0-4a79-9d08-c989c50d62cb", - "idName": "timeZone", - "type": "QString", - "defaultValue": "-" - }, - { - "name": "lon", - "id": "5a3a54d3-afd4-464a-adba-23def0110ed7", - "idName": "lon", - "type": "double", - "defaultValue": 0 - }, - { - "name": "lat", - "id": "f7b52b93-688d-47bb-83cc-85a694f33537", - "idName": "lat", - "type": "double", - "defaultValue": 0 - } - ], - "actionTypes": [ - { - "name": "update", - "id": "0b4751ca-f126-4369-bfc0-f745985ae59b", - "idName": "update" - } - ] - } - ] - } - ] - } - \endcode - - Once you have changed \l{The Plugin JSON file} you should rebuild the whole project to make shore all changed will be considerated. In the \e {Qt Creator} got to the menu \unicode{0x2192} \b Build \unicode{0x2192} \b{Rebuild all} to create the new \tt plugininfo.h file. You should see in the build output something like this: - - \code - /usr/bin/nymea-generateplugininfo ../networkinfo-diy/devicepluginnetworkinfo.json plugininfo.h - ../networkinfo-diy/devicepluginnetworkinfo.json -> plugininfo.h - --> generate plugininfo.h - PluginId for plugin "Network Info" = c16852d7-f123-4dd5-983d-fc2eedb885aa - define VendorId NetworkInfoVendorId = 2062d64d-3232-433c-88bc-0d33c0ba2ba6 - define DeviceClassId infoDeviceClassId = 6c9d4852-cdfa-4eba-9ff2-c084d6f9d756 - define StateTypeId addressStateTypeId = 0b4751ca-f126-4369-bfc0-f745985ae59b - define StateTypeId cityStateTypeId = 8c777cf7-1a54-4b80-a8fe-141ae2334a63 - define StateTypeId countryStateTypeId = 69a01d64-c68f-4175-85f3-69329fd66b52 - define StateTypeId timeZoneStateTypeId = ab5278ce-87e0-4a79-9d08-c989c50d62cb - define StateTypeId lonStateTypeId = 5a3a54d3-afd4-464a-adba-23def0110ed7 - define StateTypeId latStateTypeId = f7b52b93-688d-47bb-83cc-85a694f33537 - define logging category: "dcNetworkInfo" - --> generated successfully "plugininfo.h" - --> generate extern-plugininfo.h - --> generated successfully "extern-plugininfo.h" - \endcode - - \note You have to change the \tt {qCDebug(dcMinimal)} \unicode{0x2192} \tt {qCDebug(dcNetworkInfo)} because you have changed the plugin \e idName and therefore also the logging categorie. You need to start nymea now with the parameter \b {\tt {nymead -n -d NetworkInfo}} to see the debug output of the new plugin. - - If you make a syntax error in the JSON file, you will get a build error with the position of the syntax error in the JSON file. Now your definitions should be in the plugininfo.h file and ready to use in the plugin source code. - - \section1 Writing the plugin - - Now we have our basic for starting to implement the new defined plugin. If you install the current plugin, start \tt nymead and add the a \b {Info about Network} device with \b {\tt nymea-cli} you can check the device states and should see something like this: - - \code - ======================================================== - -> States of device "Info about Network" {83a1c0bb-c169-4292-a100-85af5fa9a1a4}: - - ip address: - - city: - - country: - - time zone: - - lon: 0 - lat: 0 - -------------------------------------------------------- - - \endcode - - All defined states are already availabe in the system and initialized with the \e defaultValue -parameter from \l{The Plugin JSON file}. - - \section2 Define the required hardware resource - Now we have to fetch the data from \l{http://ip-api.com/json} once the action \tt update will be executed. The first thing we have to define is the hardware resource. Since we are communicating with a REST API we need the \l{NetworkManager} hardware resource, which is basically a \l{http://doc.qt.io/qt-5/qnetworkaccessmanager.html}{QNetworkAccessManager} for all plugins. - - \code - DeviceManager::HardwareResources DevicePluginNetworkInfo::requiredHardware() const - { - return DeviceManager::HardwareResourceNetworkManager; - } - \endcode - - - \section2 Implement executeAction method - The next verry important method we have to implement and override is the \l{DevicePlugin::executeAction()} method, which will be calle when the user wants to execute a certain \l{Action}. - - \code - DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; - \endcode - - The implementation looks like this: - \code - DeviceManager::DeviceError DevicePluginNetworkInfo::executeAction(Device *device, const Action &action) - { - // check if this device is a Network info device using the DeviceClassId - if (device->deviceClassId() != infoDeviceClassId) { - return DeviceManager::DeviceErrorDeviceClassNotFound; - } - - // check if the requested action is our "update" action ... - if (action.actionTypeId() == updateActionTypeId) { - - // Print information that we are executing now the update action - qCDebug(dcNetworkInfo) << "Execute update action" << action.id(); - - // Create a network request - QNetworkRequest locationRequest(QUrl("http://ip-api.com/json")); - - // Call the GET method from the NetworkManager - QNetworkReply *reply = networkManagerGet(locationRequest); - - // Hash the reply, because we don't get the result immediately - m_asyncActionReplies.insert(reply, action.id()); - - // Hash the device for this action - m_asyncActions.insert(action.id(), device); - - // Tell the DeviceManager that this is an async action and the result of the execution will - // be emitted later. - return DeviceManager::DeviceErrorAsync; - } - - // ...otherwise the ActionType does not exist - return DeviceManager::DeviceErrorActionTypeNotFound; - } - \endcode - - \section2 Implement networkManagerReplyReady method - Once the result of your pending network request is finished, the method \l{DevicePlugin::networkManagerReplyReady()} will be called, so we have to implement this method in our plugin header file and override the method: - \code - void networkManagerReplyReady(QNetworkReply *reply) override; - \endcode - - The implementation looks like this: - \code - // This method will be called whenever the reply from a NetworkManager call is ready. - void DevicePluginNetworkInfo::networkManagerReplyReady(QNetworkReply *reply) - { - // Make shore this is our reply - if (!m_asyncActionReplies.keys().contains(reply)) - return; - - // This is one of our action replies!! - - // Take the corresponding action from our hash - ActionId actionId = m_asyncActionReplies.take(reply); - - // Check the status code of the reply - if (reply->error()) { - - // Print the warning message - qCWarning(dcNetworkInfo) << "Reply error" << reply->errorString(); - - // The action execution is finished, and was not successfully - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable); - - // Important -> delete the reply to prevent a memory leak! - reply->deleteLater(); - return; - } - - // The request was successful, lets read the payload - QByteArray data = reply->readAll(); - - // Important -> delete the reply to prevent a memory leak! - reply->deleteLater(); - - // Process the data from the reply - actionDataReady(actionId, data); - } - \endcode - - \section2 Update the state values - - Once the reply was read successfully we have to read the json document and set our state values to the fetched values. For this we implement a private method called: - - \code - void actionDataReady(const ActionId &actionId, const QByteArray &data); - \endcode - - First we have to check if the received data is a valid JSON document. If not, the action execution \b "update" was not successful and we have to report the error. Otherwise we read the data and set the state values of our device. - - \code - void DevicePluginNetworkInfo::actionDataReady(const ActionId &actionId, const QByteArray &data) - { - // Convert the rawdata to a json document - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - // Check if we got a valid JSON document - if(error.error != QJsonParseError::NoError) { - qCWarning(dcNetworkInfo) << "Failed to parse JSON data" << data << ":" << error.errorString(); - - // the action execution is finished, and was not successfully - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); - return; - } - - // print the fetched data in json format to stdout - qCDebug(dcNetworkInfo) << jsonDoc.toJson(); - - // Get the device for this action - Device *device = m_asyncActions.take(actionId); - - // Parse the data and update the states of our device - QVariantMap dataMap = jsonDoc.toVariant().toMap(); - - // Set the city state - if (dataMap.contains("city")) { - device->setStateValue(cityStateTypeId, dataMap.value("city").toString()); - } - - // Set the country state - if (dataMap.contains("countryCode")) { - device->setStateValue(countryStateTypeId, dataMap.value("countryCode").toString()); - } - - // Set the wan ip - if (dataMap.contains("query")) { - device->setStateValue(addressStateTypeId, dataMap.value("query").toString()); - } - - // Set the time zone state - if (dataMap.contains("timezone")) { - device->setStateValue(timeZoneStateTypeId, dataMap.value("timezone").toString()); - } - - // Set the longitude state - if (dataMap.contains("lon")) { - device->setStateValue(lonStateTypeId, dataMap.value("lon").toDouble()); - } - - // Set the latitude state - if (dataMap.contains("lat")) { - device->setStateValue(latStateTypeId, dataMap.value("lat").toDouble()); - } - - qCDebug(dcNetworkInfo) << "Action" << actionId << "execution finished successfully."; - - // Emit the successful action execution result to the device manager - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); - } - \endcode - - You can find the full example in the \tt plugin-templates \unicode{0x2192} \tt networkinfo folder. - - \section1 Test the plugin - - Rebuild the whole project to make shore all changes are registered and install the plugin (see \l{Install the plugin}{Tutorial 1 - Install the plugin}). - - \list 1 - \li Start nymea with following command: - - \code - $ nymead -n -d NetworkInfo - \endcode - - \li Start nymea-cli and add the a new "Info" devcice. - \li Use nymea-cli to check if the device states are initialized with the default values from \l{Change the devicepluginnetworkinfo.json}: - - \tt "Devices" \unicode{0x2192} \tt "List..." \unicode{0x2192} \tt {"List device states"} \unicode{0x2192} \tt {"Your device name"}. - \li Use nymea-cli to execute the \b update action: - - \tt "Devices" \unicode{0x2192} \tt "Execute action" \unicode{0x2192} \tt {"Your device name"} \unicode{0x2192} \tt {update} - \li Use nymea-cli to check if the device states were updated successfully: - - \tt "Devices" \unicode{0x2192} \tt "List..." \unicode{0x2192} \tt {"List device states"} \unicode{0x2192} \tt {"Your device name"}. - - \endlist -*/ - - diff --git a/doc/tutorial6.qdoc b/doc/tutorial6.qdoc deleted file mode 100644 index ba820702..00000000 --- a/doc/tutorial6.qdoc +++ /dev/null @@ -1,434 +0,0 @@ -/*! - \page tutorial6.html - \title Tutorial 6 - The "CoAP Client" plugin - \brief The plugin shows you how to use the CoAP lib - \ingroup tutorials - - \section1 Topics - This tutorial will show you how to: - \list - \li \unicode{0x25B6} Allow only one \l{Device} - \li \unicode{0x25B6} Implement the \l{DevicePlugin::deviceRemoved()}{deviceRemoved()} method - \li \unicode{0x25B6} Use the \l{Coap}{CoAP} library - \endlist - - This tutorial shows you how to write a \l{Coap}{CoAP} plugin and how the plugin configuration work. The plugin it self has no practical purpose but shows some concepts of CoAP and plugin development. - - \section1 The plugin source code - - \section2 networkinfo.pro - - \code - include(plugins.pri) - - TARGET = nymea_deviceplugincoapclient - - message(============================================) - message("Qt version: $$[QT_VERSION]") - message("Building $$deviceplugin$${TARGET}.so") - - SOURCES += \ - deviceplugincoapclient.cpp \ - - HEADERS += \ - deviceplugincoapclient.h \ - - \endcode - - \section2 devicepluginnetworkinfo.json - - \code - { - "name": "Coap Client", - "idName": "CoapClient", - "id": "9ecadcbb-8699-41c2-a2e3-fd51a1faf1a1", - "paramTypes": [ - { - "name": "url", - "type": "QString", - "inputType": "TextLine", - "defaultValue": "coap://vs0.inf.ethz.ch:5683" - } - ], - "vendors": [ - { - "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", - "name": "nymea", - "idName": "nymea", - "deviceClasses": [ - { - "deviceClassId": "69dcccbd-a66a-4c5b-8921-2fb86c4c4299", - "idName": "info", - "name": "Coap Client", - "createMethods": ["user"], - "basicTags": [ - "Service", - "Sensor", - "Actuator" - ], - "stateTypes": [ - { - "id": "b8433a82-cf83-424f-b4a2-3f6507405d6c", - "idName": "notifications", - "name": "notification", - "type": "bool", - "defaultValue": false, - "writable": true - } - ], - "actionTypes": [ - { - "id": "9aa31838-b62f-43b3-bdcd-8165840b5edf", - "name": "upload message", - "idName": "upload", - "paramTypes": [ - { - "name": "message", - "type": "QString", - "defaultValue": "Hallo world!" - } - ] - } - ], - "eventTypes": [ - { - "name": "time changed", - "idName": "time", - "id": "44513802-138e-42f8-86a6-9edd4df77535", - "paramTypes": [ - { - "name": "time", - "type": "QString" - } - ] - } - ] - } - ] - } - ] - } - \endcode - - \section2 devicepluginnetworkinfo.h - - \code - #ifndef DEVICEPLUGINCOAPCLIENT_H - #define DEVICEPLUGINCOAPCLIENT_H - - #include "devicemanager.h" - #include "plugin/deviceplugin.h" - #include "coap/coap.h" - - #include - #include - - class DevicePluginCoapClient : public DevicePlugin - { - Q_OBJECT - - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugincoapclient.json") - Q_INTERFACES(DevicePlugin) - - public: - explicit DevicePluginCoapClient(); - - DeviceManager::HardwareResources requiredHardware() const override; - DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - void deviceRemoved(Device *device) override; - - DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; - - private: - QPointer m_device; - QPointer m_coap; - - // Replies from coap - QHash m_discoverReplies; - QHash m_notificationEnableReplies; - QHash m_notificationDisableReplies; - QList m_uploadReplies; - - QHash< CoapReply *, ActionId> m_asyncActions; - - private slots: - void onReplyFinished(CoapReply *reply); - void onNotificationReceived(const CoapObserveResource &resource, const int ¬ificationNumber, const QByteArray &payload); - - }; - - #endif // DEVICEPLUGINNETWORKINFO_H - \endcode - - \section2 devicepluginnetworkinfo.cpp - - \code - #include "deviceplugincoapclient.h" - #include "plugininfo.h" - - #include - - #include "coap/corelinkparser.h" - - // Note: You can find the documentation for this code here -> http://doc.nymea.io/write-plugins.html - - // The constructor of this device plugin. - DevicePluginCoapClient::DevicePluginCoapClient() - { - } - - DeviceManager::HardwareResources DevicePluginCoapClient::requiredHardware() const - { - return DeviceManager::HardwareResourceNone; - } - - DeviceManager::DeviceSetupStatus DevicePluginCoapClient::setupDevice(Device *device) - { - // Check if we already have a coap client device - if (!myDevices().isEmpty()) { - qCWarning(dcCoapClient) << "There is already a configured coap client device"; - return DeviceManager::DeviceSetupStatusFailure; - } - - qCDebug(dcCoapClient) << "Setting up a new device:" << device->name() << device->params(); - - // Verify the given URL - QUrl url(device->paramValue("url").toString()); - if (url.scheme() != "coap") { - qCWarning(dcCoapClient) << "Invalid URL scheme" << url.scheme() << " != " << "coap"; - return DeviceManager::DeviceSetupStatusFailure; - } - - m_device = device; - - // Create new CoAP client if there isn't one yet - if (m_coap.isNull()) { - m_coap = new Coap(this); - connect(m_coap, &Coap::replyFinished, this, &DevicePluginCoapClient::onReplyFinished); - connect(m_coap, &Coap::notificationReceived, this, &DevicePluginCoapClient::onNotificationReceived); - } - - // Discover the CoAP server - url.setPath("/.well-known/core"); - CoapReply *reply = m_coap->get(CoapRequest(url)); - - // Check immediately if the there occurred any error - if (reply->error() != CoapReply::NoError) { - qCWarning(dcCoapClient) << "Could not discover CoAP server:" << reply->errorString(); - reply->deleteLater(); - m_coap->deleteLater(); - return DeviceManager::DeviceSetupStatusFailure; - } - - // Store the reply and device until we get our asynchronous response - m_discoverReplies.insert(reply, device); - - // Tell the DeviceManager that the setup result will be communicated later - return DeviceManager::DeviceSetupStatusAsync; - } - - void DevicePluginCoapClient::deviceRemoved(Device *device) - { - // Prevent the unused variable warning - Q_UNUSED(device) - - // Delete the CoAP socket if not longer needed - m_coap->deleteLater(); - } - - // This method will be called whenever a client or the rule engine wants to execute an action for the given device. - DeviceManager::DeviceError DevicePluginCoapClient::executeAction(Device *device, const Action &action) - { - qCDebug(dcCoapClient) << "Execute action" << action.id() << action.params(); - - // check if the requested action is our "upload" action ... - if (action.actionTypeId() == notificationsActionTypeId) { - - // observe resource (enable notifications) - QUrl url(device->paramValue("url").toString()); - url.setPath(url.path().append("/obs")); - - if (action.param("notification").value().toBool()) { - qCDebug(dcCoapClient) << "Enable notification on resource" << url.toString(); - CoapReply *reply = m_coap->enableResourceNotifications(CoapRequest(url)); - m_asyncActions.insert(reply, action.id()); - m_notificationEnableReplies.insert(reply, device); - } else { - qCDebug(dcCoapClient) << "Disable notification on resource" << url.toString(); - CoapReply *reply = m_coap->disableNotifications(CoapRequest(url)); - m_asyncActions.insert(reply, action.id()); - m_notificationDisableReplies.insert(reply, device); - } - - // Tell the DeviceManager that this is an async action and the - // result of the execution will be emitted later. - return DeviceManager::DeviceErrorAsync; - - } else if (action.actionTypeId() == uploadActionTypeId) { - - // Define the URL for uploading the message (POST) - QUrl url(device->paramValue("url").toString()); - url.setPath(url.path().append("/test")); - - // Upload the message (POST) - CoapReply *reply = m_coap->post(CoapRequest(url), action.param("message").value().toString().toUtf8()); - m_uploadReplies.append(reply); - m_asyncActions.insert(reply, action.id()); - - // Tell the DeviceManager that this is an async action and the - // result of the execution will be emitted later. - return DeviceManager::DeviceErrorAsync; - } - - // ...otherwise the ActionType does not exist - return DeviceManager::DeviceErrorActionTypeNotFound; - } - - // This slot will be called whenever a reply from the CoAP socket has finished - void DevicePluginCoapClient::onReplyFinished(CoapReply *reply) - { - // Now check which reply this was by checking in which Hash it can be found - if (m_discoverReplies.keys().contains(reply)) { - Device *device = m_discoverReplies.take(reply); - - // Verify there where no reply errors (transport layer) - if (reply->error() != CoapReply::NoError) { - qCWarning(dcCoapClient) << "CoAP resource discovery reply error" << reply->errorString(); - reply->deleteLater(); - // Something went wrong during the discovery. Finish the setup with error. - emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); - return; - } - - // Verify we have the right status code (server response) - if (reply->statusCode() != CoapPdu::Content) { - qCWarning(dcCoapClient) << "CoAP discovery status code:" << reply; - reply->deleteLater(); - // Something went wrong during the discovery. Finish the setup with error. - emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); - return; - } - - qCDebug(dcCoapClient) << "Discovered successfully the resources"; - - // Print the CoRE links we got from the server resource discovery - CoreLinkParser parser(reply->payload()); - foreach (const CoreLink &link, parser.links()) { - qCDebug(dcCoapClient) << link << endl; - } - - // Tell the device manager that the device setup finished successfully - emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess); - - } else if (m_notificationEnableReplies.keys().contains(reply)) { - Device *device = m_notificationEnableReplies.take(reply); - ActionId actionId = m_asyncActions.take(reply); - - // Verify there where no reply errors (transport layer) - if (reply->error() != CoapReply::NoError) { - qCWarning(dcCoapClient) << "CoAP enable observe resource reply error" << reply->errorString(); - // Something went wrong. Tell the devicemanager that the action finished with error. - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); - reply->deleteLater(); - return; - } - - // Verify we have the right status code (server response) - if (reply->statusCode() != CoapPdu::Content) { - qCWarning(dcCoapClient) << "CoAP enable observe status code:" << reply; - // Something went wrong. Tell the devicemanager that the action finished with error. - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); - reply->deleteLater(); - return; - } - - qCDebug(dcCoapClient) << "Enabled successfully notifications" << reply; - - // Set the corresping state - device->setStateValue(notificationsStateTypeId, true); - - // Tell the device manager that the action execution finished successfully - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); - - } else if (m_notificationDisableReplies.keys().contains(reply)) { - Device *device = m_notificationDisableReplies.take(reply); - ActionId actionId = m_asyncActions.take(reply); - - // Verify there where no reply errors (transport layer) - if (reply->error() != CoapReply::NoError) { - qCWarning(dcCoapClient) << "CoAP disable observe resource reply error" << reply->errorString(); - // Something went wrong. Tell the devicemanager that the action finished with error. - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); - reply->deleteLater(); - return; - } - - // Verify we have the right status code (server response) - if (reply->statusCode() != CoapPdu::Content) { - qCWarning(dcCoapClient) << "CoAP disable observe status code:" << reply; - // Something went wrong. Tell the devicemanager that the action finished with error. - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); - reply->deleteLater(); - return; - } - - qCDebug(dcCoapClient) << "Disabled successfully notifications" << reply; - - // Set the corresping state - device->setStateValue(notificationsStateTypeId, false); - - // Tell the device manager that the action execution finished successfully - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); - - } else if (m_uploadReplies.contains(reply)) { - ActionId actionId = m_asyncActions.take(reply); - - // Verify there where no reply errors (transport layer) - if (reply->error() != CoapReply::NoError) { - qCWarning(dcCoapClient) << "CoAP upload reply error" << reply->errorString(); - // Something went wrong. Tell the devicemanager that the action finished with error. - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); - reply->deleteLater(); - return; - } - - // Verify we have the right status code (server response) - if (reply->statusCode() != CoapPdu::Created) { - qCWarning(dcCoapClient) << "CoAP upload status code:" << reply; - // Something went wrong. Tell the devicemanager that the action finished with error. - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); - reply->deleteLater(); - return; - } - - qCDebug(dcCoapClient) << "Uploaded message successfully" << reply; - - // Tell the device manager that the action execution finished successfully - emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); - - } - - // Always make sure the reply will be deleted - reply->deleteLater(); - } - - // This method will be called if the CoAP socket received a notification from an observed resource - void DevicePluginCoapClient::onNotificationReceived(const CoapObserveResource &resource, const int ¬ificationNumber, const QByteArray &payload) - { - qCDebug(dcCoapClient) << "Got notification from observed resource" << notificationNumber << resource.url().path() << endl << payload; - - // Create the params for the event - ParamList paramList; - paramList.append(Param("time", payload)); - - // Tell the device manager we got an event - emitEvent(Event(timeEventTypeId, m_device->id(), paramList)); - } - \endcode - - \section1 Test the plugin - - -*/ - - diff --git a/doc/tutorials.qdoc b/doc/tutorials.qdoc deleted file mode 100644 index 79423522..00000000 --- a/doc/tutorials.qdoc +++ /dev/null @@ -1,11 +0,0 @@ -/*! - \page tutorials.html - \title Tutorials - \contentspage {Tutorials} - \nextpage {Tutorial 1 - The "Minimal" plugin} - \previouspage {Write your own plugin} - - In order to getting started with your own \l DevicePlugin it is reccommended to walk trough following tutorials to understand how the mechanism works: - - \annotatedlist tutorials -*/ diff --git a/doc/write-plugins.qdoc b/doc/write-plugins.qdoc index 94d3ca7b..1544997b 100644 --- a/doc/write-plugins.qdoc +++ b/doc/write-plugins.qdoc @@ -8,40 +8,7 @@ \li \l{The plugin JSON File} \li \l{CreateMethods and SetupMethods} \li \l{Testing your plugin} - \li \l{Tutorials} - \list - \li \l{Tutorial 1 - The "Minimal" plugin} - \list - \li Open and edit the project - \li Build and load the first plugin. - \endlist - \li \l{Tutorial 2 - The "Buttons" plugin} - \list - \li Start with a new \l{DevicePlugin}{Plugin} - \li Implement an \l{Action} - \li Implement an \l{Event} - \endlist - \li \l{Tutorial 3 - The "Power Button" device} - \list - \li Implement a \l{State} - \li Use \l{Param}{Params} in an \l{Event} - \li Use \l{Param}{Params} in an \l{Action} - \endlist - \li \l{Tutorial 4 - The alternative "Power Button"} - \list - \li Implement a writable \l{State} (which can be manipulated by an \l{Action}) - \endlist - \li \l{Tutorial 5 - The "Network Info" plugin} - \list - \li Use hardware resource \l{NetworkManager} - \endlist - \li \l{Tutorial 6 - The "CoAP Client" plugin} - \list - \li Allow only one \l{Device} - \li Implement the \l{DevicePlugin::deviceRemoved()}{deviceRemoved()} method - \li Use the \l{Coap}{CoAP} library - \endlist - \endlist + \li \l{Plugin tutorials} \endlist */