diff --git a/COPYING b/COPYING
new file mode 120000
index 0000000..2eac441
--- /dev/null
+++ b/COPYING
@@ -0,0 +1 @@
+LICENSE.GPL3
\ No newline at end of file
diff --git a/COPYING.LESSER b/COPYING.LESSER
new file mode 120000
index 0000000..dc71033
--- /dev/null
+++ b/COPYING.LESSER
@@ -0,0 +1 @@
+LICENSE.LGPL3
\ No newline at end of file
diff --git a/LICENSE.GPL3 b/LICENSE.GPL3
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE.GPL3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state 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 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/LICENSE.LGPL3 b/LICENSE.LGPL3
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/LICENSE.LGPL3
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ 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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU 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 as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/eastron/eastron.pro b/eastron/eastron.pro
new file mode 100644
index 0000000..314b37c
--- /dev/null
+++ b/eastron/eastron.pro
@@ -0,0 +1,14 @@
+include(../plugins.pri)
+
+# Generate modbus connections
+MODBUS_CONNECTIONS += sdm630-registers.json
+MODBUS_CONNECTIONS += sdm72-registers.json
+MODBUS_CONNECTIONS += sdm120-registers.json
+#MODBUS_TOOLS_CONFIG += VERBOSE
+include(../modbus.pri)
+
+HEADERS += \
+ integrationplugineastron.h
+
+SOURCES += \
+ integrationplugineastron.cpp
diff --git a/eastron/integrationplugineastron.cpp b/eastron/integrationplugineastron.cpp
new file mode 100644
index 0000000..80aefa3
--- /dev/null
+++ b/eastron/integrationplugineastron.cpp
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright (C) 2013 - 2024, nymea GmbH
+* Copyright (C) 2025, ETM-Schurig SARL
+*
+* This file is part of nymea-plugins-modbus.
+*
+* nymea-plugins-modbus is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* nymea-plugins-modbus is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with nymea-plugins-modbus. If not, see .
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "integrationplugineastron.h"
+#include "plugininfo.h"
+
+IntegrationPluginEastron::IntegrationPluginEastron()
+{
+}
+
+void IntegrationPluginEastron::init()
+{
+ connect(hardwareManager()->modbusRtuResource(), &ModbusRtuHardwareResource::modbusRtuMasterRemoved,
+ this, [=](const QUuid &modbusUuid) {
+ qCDebug(dcEastron()) << "Modbus RTU master removed:" << modbusUuid.toString();
+
+ foreach (Thing *thing, myThings()) {
+ ThingClassId classId = thing->thingClassId();
+
+ if (classId == sdm630ThingClassId || classId == sdm630ConsumerThingClassId || classId == sdm630ProducerThingClassId) {
+ if (thing->paramValue(sdm630ThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm630ConnectedStateTypeId, false);
+ delete m_sdm630Connections.take(thing);
+ }
+ } else if (classId == sdm630ConsumerThingClassId) {
+ if (thing->paramValue(sdm630ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm630ConsumerConnectedStateTypeId, false);
+ delete m_sdm630Connections.take(thing);
+ }
+ } else if (classId == sdm630ProducerThingClassId) {
+ if (thing->paramValue(sdm630ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm630ProducerConnectedStateTypeId, false);
+ delete m_sdm630Connections.take(thing);
+ }
+ } else if (classId == sdm72ThingClassId) {
+ if (thing->paramValue(sdm72ThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm72ConnectedStateTypeId, false);
+ delete m_sdm72Connections.take(thing);
+ }
+ } else if (classId == sdm72ConsumerThingClassId) {
+ if (thing->paramValue(sdm72ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm72ConsumerConnectedStateTypeId, false);
+ delete m_sdm72Connections.take(thing);
+ }
+ } else if (classId == sdm72ProducerThingClassId) {
+ if (thing->paramValue(sdm72ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm72ProducerConnectedStateTypeId, false);
+ delete m_sdm72Connections.take(thing);
+ }
+ } else if (classId == sdm120ThingClassId) {
+ if (thing->paramValue(sdm120ThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm120ConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm120ConsumerThingClassId) {
+ if (thing->paramValue(sdm120ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm120ConsumerConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm120ProducerThingClassId) {
+ if (thing->paramValue(sdm120ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm120ProducerConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm220ThingClassId) {
+ if (thing->paramValue(sdm220ThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm220ConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm220ConsumerThingClassId) {
+ if (thing->paramValue(sdm220ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm220ConsumerConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm220ProducerThingClassId) {
+ if (thing->paramValue(sdm220ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm220ProducerConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm230ThingClassId) {
+ if (thing->paramValue(sdm230ThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm230ConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm230ConsumerThingClassId) {
+ if (thing->paramValue(sdm230ConsumerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm230ConsumerConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ } else if (classId == sdm230ProducerThingClassId) {
+ if (thing->paramValue(sdm230ProducerThingModbusMasterUuidParamTypeId) == modbusUuid) {
+ thing->setStateValue(sdm230ProducerConnectedStateTypeId, false);
+ delete m_sdm120Connections.take(thing);
+ }
+ }
+ }
+ });
+}
+
+void IntegrationPluginEastron::discoverThings(ThingDiscoveryInfo *info)
+{
+ ThingClassId classId = info->thingClassId();
+ QString modelName;
+ ParamTypeId discoverySlaveParamTypeId;
+ ParamTypeId thingSlaveParamTypeId;
+ ParamTypeId thingMasterUuidParamTypeId;
+
+ if (classId == sdm630ThingClassId) {
+ modelName = "SDM630";
+ discoverySlaveParamTypeId = sdm630DiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm630ThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm630ThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm630ConsumerThingClassId) {
+ modelName = "SDM630";
+ discoverySlaveParamTypeId = sdm630ConsumerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm630ConsumerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm630ConsumerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm630ProducerThingClassId) {
+ modelName = "SDM630";
+ discoverySlaveParamTypeId = sdm630ProducerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm630ProducerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm630ProducerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm72ThingClassId) {
+ modelName = "SDM72";
+ discoverySlaveParamTypeId = sdm72DiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm72ThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm72ThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm72ConsumerThingClassId) {
+ modelName = "SDM72";
+ discoverySlaveParamTypeId = sdm72ConsumerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm72ConsumerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm72ConsumerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm72ProducerThingClassId) {
+ modelName = "SDM72";
+ discoverySlaveParamTypeId = sdm72ProducerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm72ProducerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm72ProducerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm120ThingClassId) {
+ modelName = "SDM120";
+ discoverySlaveParamTypeId = sdm120DiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm120ThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm120ThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm120ConsumerThingClassId) {
+ modelName = "SDM120";
+ discoverySlaveParamTypeId = sdm120ConsumerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm120ConsumerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm120ConsumerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm120ProducerThingClassId) {
+ modelName = "SDM120";
+ discoverySlaveParamTypeId = sdm120ProducerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm120ProducerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm120ProducerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm220ThingClassId) {
+ modelName = "SDM220";
+ discoverySlaveParamTypeId = sdm220DiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm220ThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm220ThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm220ConsumerThingClassId) {
+ modelName = "SDM220";
+ discoverySlaveParamTypeId = sdm220ConsumerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm220ConsumerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm220ConsumerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm220ProducerThingClassId) {
+ modelName = "SDM220";
+ discoverySlaveParamTypeId = sdm220ProducerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm220ProducerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm220ProducerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm230ThingClassId) {
+ modelName = "SDM230";
+ discoverySlaveParamTypeId = sdm230DiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm230ThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm230ThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm230ConsumerThingClassId) {
+ modelName = "SDM230";
+ discoverySlaveParamTypeId = sdm230ConsumerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm230ConsumerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm230ConsumerThingModbusMasterUuidParamTypeId;
+ } else if (classId == sdm230ProducerThingClassId) {
+ modelName = "SDM230";
+ discoverySlaveParamTypeId = sdm230ProducerDiscoverySlaveAddressParamTypeId;
+ thingSlaveParamTypeId = sdm230ProducerThingSlaveAddressParamTypeId;
+ thingMasterUuidParamTypeId = sdm230ProducerThingModbusMasterUuidParamTypeId;
+ } else {
+ info->finish(Thing::ThingErrorThingClassNotFound);
+ return;
+ }
+
+ discoverRtuDevices(info, modelName, discoverySlaveParamTypeId, thingSlaveParamTypeId, thingMasterUuidParamTypeId);
+}
+
+void IntegrationPluginEastron::discoverRtuDevices(ThingDiscoveryInfo *info, const QString &modelName,
+ const ParamTypeId &discoverySlaveParamTypeId,
+ const ParamTypeId &thingSlaveParamTypeId,
+ const ParamTypeId &thingMasterUuidParamTypeId)
+{
+ if (hardwareManager()->modbusRtuResource()->modbusRtuMasters().isEmpty()) {
+ info->finish(Thing::ThingErrorHardwareNotAvailable,
+ QT_TR_NOOP("No Modbus RTU interface available. Please set up the Modbus RTU interface first."));
+ return;
+ }
+
+ uint slaveAddress = info->params().paramValue(discoverySlaveParamTypeId).toUInt();
+ if (slaveAddress == 0 || slaveAddress > 254) {
+ info->finish(Thing::ThingErrorInvalidParameter,
+ QT_TR_NOOP("The Modbus slave address must be a value between 1 and 254."));
+ return;
+ }
+
+ foreach (ModbusRtuMaster *modbusMaster, hardwareManager()->modbusRtuResource()->modbusRtuMasters()) {
+ qCDebug(dcEastron()) << "Found RTU master" << modbusMaster << "connected:" << modbusMaster->connected();
+ if (!modbusMaster->connected())
+ continue;
+
+ ThingDescriptor descriptor(info->thingClassId(), modelName,
+ QString::number(slaveAddress) + " " + modbusMaster->serialPort());
+ ParamList params;
+ params << Param(thingSlaveParamTypeId, slaveAddress);
+ params << Param(thingMasterUuidParamTypeId, modbusMaster->modbusUuid());
+ descriptor.setParams(params);
+ info->addThingDescriptor(descriptor);
+ }
+
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginEastron::setupThing(ThingSetupInfo *info)
+{
+ ThingClassId classId = info->thing()->thingClassId();
+
+ if (classId == sdm630ThingClassId || classId == sdm630ConsumerThingClassId || classId == sdm630ProducerThingClassId) {
+ setupSdm630(info);
+ } else if (classId == sdm72ThingClassId || classId == sdm72ConsumerThingClassId || classId == sdm72ProducerThingClassId) {
+ setupSdm72(info);
+ } else if (classId == sdm120ThingClassId || classId == sdm120ConsumerThingClassId || classId == sdm120ProducerThingClassId
+ || classId == sdm220ThingClassId || classId == sdm220ConsumerThingClassId || classId == sdm220ProducerThingClassId
+ || classId == sdm230ThingClassId || classId == sdm230ConsumerThingClassId || classId == sdm230ProducerThingClassId) {
+ setupSdm120(info);
+ } else {
+ info->finish(Thing::ThingErrorThingClassNotFound);
+ }
+}
+
+void IntegrationPluginEastron::setupSdm630(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ ThingClassId classId = thing->thingClassId();
+
+ ParamTypeId slaveParamTypeId;
+ ParamTypeId masterUuidParamTypeId;
+ StateTypeId connectedStateTypeId;
+
+ if (classId == sdm630ThingClassId) {
+ slaveParamTypeId = sdm630ThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm630ThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm630ConnectedStateTypeId;
+ } else if (classId == sdm630ConsumerThingClassId) {
+ slaveParamTypeId = sdm630ConsumerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm630ConsumerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm630ConsumerConnectedStateTypeId;
+ } else {
+ slaveParamTypeId = sdm630ProducerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm630ProducerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm630ProducerConnectedStateTypeId;
+ }
+
+ uint address = thing->paramValue(slaveParamTypeId).toUInt();
+ if (address == 0 || address > 254) {
+ qCWarning(dcEastron()) << "Setup failed, invalid slave address" << address;
+ info->finish(Thing::ThingErrorSetupFailed,
+ QT_TR_NOOP("The Modbus address not valid. It must be a value between 1 and 254."));
+ return;
+ }
+
+ QUuid uuid = thing->paramValue(masterUuidParamTypeId).toUuid();
+ if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
+ qCWarning(dcEastron()) << "Setup failed, Modbus RTU master not available";
+ info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU interface not available."));
+ return;
+ }
+
+ if (m_sdm630Connections.contains(thing))
+ m_sdm630Connections.take(thing)->deleteLater();
+
+ Sdm630ModbusRtuConnection *connection = new Sdm630ModbusRtuConnection(
+ hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this);
+
+ connect(connection, &Sdm630ModbusRtuConnection::reachableChanged, this, [=](bool reachable) {
+ thing->setStateValue(connectedStateTypeId, reachable);
+ });
+
+ if (classId == sdm630ThingClassId) {
+ connect(connection, &Sdm630ModbusRtuConnection::voltagePhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm630VoltagePhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::voltagePhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm630VoltagePhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::voltagePhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm630VoltagePhaseCStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::currentPhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm630CurrentPhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::currentPhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm630CurrentPhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::currentPhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm630CurrentPhaseCStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
+ thing->setStateValue(sdm630CurrentPowerStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::powerPhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm630CurrentPowerPhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::powerPhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm630CurrentPowerPhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::powerPhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm630CurrentPowerPhaseCStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::frequencyChanged, this, [=](float v) {
+ thing->setStateValue(sdm630FrequencyStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
+ thing->setStateValue(sdm630TotalEnergyConsumedStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
+ thing->setStateValue(sdm630TotalEnergyProducedStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::energyConsumedPhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm630EnergyConsumedPhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::energyConsumedPhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm630EnergyConsumedPhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::energyConsumedPhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm630EnergyConsumedPhaseCStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::energyProducedPhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm630EnergyProducedPhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::energyProducedPhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm630EnergyProducedPhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::energyProducedPhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm630EnergyProducedPhaseCStateTypeId, v);
+ });
+
+ } else if (classId == sdm630ConsumerThingClassId) {
+ connect(connection, &Sdm630ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
+ thing->setStateValue(sdm630ConsumerCurrentPowerStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
+ thing->setStateValue(sdm630ConsumerTotalEnergyConsumedStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::frequencyChanged, this, [=](float v) {
+ thing->setStateValue(sdm630ConsumerFrequencyStateTypeId, v);
+ });
+
+ } else { // sdm630Producer
+ connect(connection, &Sdm630ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
+ thing->setStateValue(sdm630ProducerCurrentPowerStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
+ thing->setStateValue(sdm630ProducerTotalEnergyProducedStateTypeId, v);
+ });
+ connect(connection, &Sdm630ModbusRtuConnection::frequencyChanged, this, [=](float v) {
+ thing->setStateValue(sdm630ProducerFrequencyStateTypeId, v);
+ });
+ }
+
+ m_sdm630Connections.insert(thing, connection);
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginEastron::setupSdm72(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ ThingClassId classId = thing->thingClassId();
+
+ ParamTypeId slaveParamTypeId;
+ ParamTypeId masterUuidParamTypeId;
+ StateTypeId connectedStateTypeId;
+
+ if (classId == sdm72ThingClassId) {
+ slaveParamTypeId = sdm72ThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm72ThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm72ConnectedStateTypeId;
+ } else if (classId == sdm72ConsumerThingClassId) {
+ slaveParamTypeId = sdm72ConsumerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm72ConsumerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm72ConsumerConnectedStateTypeId;
+ } else {
+ slaveParamTypeId = sdm72ProducerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm72ProducerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm72ProducerConnectedStateTypeId;
+ }
+
+ uint address = thing->paramValue(slaveParamTypeId).toUInt();
+ if (address == 0 || address > 254) {
+ qCWarning(dcEastron()) << "Setup failed, invalid slave address" << address;
+ info->finish(Thing::ThingErrorSetupFailed,
+ QT_TR_NOOP("The Modbus address not valid. It must be a value between 1 and 254."));
+ return;
+ }
+
+ QUuid uuid = thing->paramValue(masterUuidParamTypeId).toUuid();
+ if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
+ qCWarning(dcEastron()) << "Setup failed, Modbus RTU master not available";
+ info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU interface not available."));
+ return;
+ }
+
+ if (m_sdm72Connections.contains(thing))
+ m_sdm72Connections.take(thing)->deleteLater();
+
+ Sdm72ModbusRtuConnection *connection = new Sdm72ModbusRtuConnection(
+ hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this);
+
+ connect(connection, &Sdm72ModbusRtuConnection::reachableChanged, this, [=](bool reachable) {
+ thing->setStateValue(connectedStateTypeId, reachable);
+ });
+
+ if (classId == sdm72ThingClassId) {
+ connect(connection, &Sdm72ModbusRtuConnection::voltagePhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm72VoltagePhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::voltagePhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm72VoltagePhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::voltagePhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm72VoltagePhaseCStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::currentPhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm72CurrentPhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::currentPhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm72CurrentPhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::currentPhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm72CurrentPhaseCStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
+ thing->setStateValue(sdm72CurrentPowerStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::powerPhaseAChanged, this, [=](float v) {
+ thing->setStateValue(sdm72CurrentPowerPhaseAStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::powerPhaseBChanged, this, [=](float v) {
+ thing->setStateValue(sdm72CurrentPowerPhaseBStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::powerPhaseCChanged, this, [=](float v) {
+ thing->setStateValue(sdm72CurrentPowerPhaseCStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::frequencyChanged, this, [=](float v) {
+ thing->setStateValue(sdm72FrequencyStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
+ thing->setStateValue(sdm72TotalEnergyConsumedStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
+ thing->setStateValue(sdm72TotalEnergyProducedStateTypeId, v);
+ });
+
+ } else if (classId == sdm72ConsumerThingClassId) {
+ connect(connection, &Sdm72ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
+ thing->setStateValue(sdm72ConsumerCurrentPowerStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
+ thing->setStateValue(sdm72ConsumerTotalEnergyConsumedStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::frequencyChanged, this, [=](float v) {
+ thing->setStateValue(sdm72ConsumerFrequencyStateTypeId, v);
+ });
+
+ } else { // sdm72Producer
+ connect(connection, &Sdm72ModbusRtuConnection::totalCurrentPowerChanged, this, [=](float v) {
+ thing->setStateValue(sdm72ProducerCurrentPowerStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
+ thing->setStateValue(sdm72ProducerTotalEnergyProducedStateTypeId, v);
+ });
+ connect(connection, &Sdm72ModbusRtuConnection::frequencyChanged, this, [=](float v) {
+ thing->setStateValue(sdm72ProducerFrequencyStateTypeId, v);
+ });
+ }
+
+ m_sdm72Connections.insert(thing, connection);
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginEastron::setupSdm120(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ ThingClassId classId = thing->thingClassId();
+
+ ParamTypeId slaveParamTypeId;
+ ParamTypeId masterUuidParamTypeId;
+ StateTypeId connectedStateTypeId;
+ StateTypeId voltageStateTypeId;
+ StateTypeId currentStateTypeId;
+ StateTypeId powerStateTypeId;
+ StateTypeId frequencyStateTypeId;
+ StateTypeId totalEnergyConsumedStateTypeId;
+ StateTypeId totalEnergyProducedStateTypeId;
+ bool isEnergyMeter = false;
+
+ if (classId == sdm120ThingClassId) {
+ slaveParamTypeId = sdm120ThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm120ThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm120ConnectedStateTypeId;
+ voltageStateTypeId = sdm120VoltagePhaseAStateTypeId;
+ currentStateTypeId = sdm120CurrentPhaseAStateTypeId;
+ powerStateTypeId = sdm120CurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm120FrequencyStateTypeId;
+ totalEnergyConsumedStateTypeId = sdm120TotalEnergyConsumedStateTypeId;
+ totalEnergyProducedStateTypeId = sdm120TotalEnergyProducedStateTypeId;
+ isEnergyMeter = true;
+ } else if (classId == sdm120ConsumerThingClassId) {
+ slaveParamTypeId = sdm120ConsumerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm120ConsumerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm120ConsumerConnectedStateTypeId;
+ powerStateTypeId = sdm120ConsumerCurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm120ConsumerFrequencyStateTypeId;
+ totalEnergyConsumedStateTypeId = sdm120ConsumerTotalEnergyConsumedStateTypeId;
+ } else if (classId == sdm120ProducerThingClassId) {
+ slaveParamTypeId = sdm120ProducerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm120ProducerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm120ProducerConnectedStateTypeId;
+ powerStateTypeId = sdm120ProducerCurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm120ProducerFrequencyStateTypeId;
+ totalEnergyProducedStateTypeId = sdm120ProducerTotalEnergyProducedStateTypeId;
+ } else if (classId == sdm220ThingClassId) {
+ slaveParamTypeId = sdm220ThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm220ThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm220ConnectedStateTypeId;
+ voltageStateTypeId = sdm220VoltagePhaseAStateTypeId;
+ currentStateTypeId = sdm220CurrentPhaseAStateTypeId;
+ powerStateTypeId = sdm220CurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm220FrequencyStateTypeId;
+ totalEnergyConsumedStateTypeId = sdm220TotalEnergyConsumedStateTypeId;
+ totalEnergyProducedStateTypeId = sdm220TotalEnergyProducedStateTypeId;
+ isEnergyMeter = true;
+ } else if (classId == sdm220ConsumerThingClassId) {
+ slaveParamTypeId = sdm220ConsumerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm220ConsumerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm220ConsumerConnectedStateTypeId;
+ powerStateTypeId = sdm220ConsumerCurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm220ConsumerFrequencyStateTypeId;
+ totalEnergyConsumedStateTypeId = sdm220ConsumerTotalEnergyConsumedStateTypeId;
+ } else if (classId == sdm220ProducerThingClassId) {
+ slaveParamTypeId = sdm220ProducerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm220ProducerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm220ProducerConnectedStateTypeId;
+ powerStateTypeId = sdm220ProducerCurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm220ProducerFrequencyStateTypeId;
+ totalEnergyProducedStateTypeId = sdm220ProducerTotalEnergyProducedStateTypeId;
+ } else if (classId == sdm230ThingClassId) {
+ slaveParamTypeId = sdm230ThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm230ThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm230ConnectedStateTypeId;
+ voltageStateTypeId = sdm230VoltagePhaseAStateTypeId;
+ currentStateTypeId = sdm230CurrentPhaseAStateTypeId;
+ powerStateTypeId = sdm230CurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm230FrequencyStateTypeId;
+ totalEnergyConsumedStateTypeId = sdm230TotalEnergyConsumedStateTypeId;
+ totalEnergyProducedStateTypeId = sdm230TotalEnergyProducedStateTypeId;
+ isEnergyMeter = true;
+ } else if (classId == sdm230ConsumerThingClassId) {
+ slaveParamTypeId = sdm230ConsumerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm230ConsumerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm230ConsumerConnectedStateTypeId;
+ powerStateTypeId = sdm230ConsumerCurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm230ConsumerFrequencyStateTypeId;
+ totalEnergyConsumedStateTypeId = sdm230ConsumerTotalEnergyConsumedStateTypeId;
+ } else { // sdm230Producer
+ slaveParamTypeId = sdm230ProducerThingSlaveAddressParamTypeId;
+ masterUuidParamTypeId = sdm230ProducerThingModbusMasterUuidParamTypeId;
+ connectedStateTypeId = sdm230ProducerConnectedStateTypeId;
+ powerStateTypeId = sdm230ProducerCurrentPowerStateTypeId;
+ frequencyStateTypeId = sdm230ProducerFrequencyStateTypeId;
+ totalEnergyProducedStateTypeId = sdm230ProducerTotalEnergyProducedStateTypeId;
+ }
+
+ uint address = thing->paramValue(slaveParamTypeId).toUInt();
+ if (address == 0 || address > 254) {
+ qCWarning(dcEastron()) << "Setup failed, invalid slave address" << address;
+ info->finish(Thing::ThingErrorSetupFailed,
+ QT_TR_NOOP("The Modbus address not valid. It must be a value between 1 and 254."));
+ return;
+ }
+
+ QUuid uuid = thing->paramValue(masterUuidParamTypeId).toUuid();
+ if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
+ qCWarning(dcEastron()) << "Setup failed, Modbus RTU master not available";
+ info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU interface not available."));
+ return;
+ }
+
+ if (m_sdm120Connections.contains(thing))
+ m_sdm120Connections.take(thing)->deleteLater();
+
+ Sdm120ModbusRtuConnection *connection = new Sdm120ModbusRtuConnection(
+ hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), address, this);
+
+ connect(connection, &Sdm120ModbusRtuConnection::reachableChanged, this, [=](bool reachable) {
+ thing->setStateValue(connectedStateTypeId, reachable);
+ });
+
+ connect(connection, &Sdm120ModbusRtuConnection::activePowerChanged, this, [=](float v) {
+ thing->setStateValue(powerStateTypeId, v);
+ });
+ connect(connection, &Sdm120ModbusRtuConnection::frequencyChanged, this, [=](float v) {
+ thing->setStateValue(frequencyStateTypeId, v);
+ });
+
+ if (isEnergyMeter) {
+ connect(connection, &Sdm120ModbusRtuConnection::voltageChanged, this, [=](float v) {
+ thing->setStateValue(voltageStateTypeId, v);
+ });
+ connect(connection, &Sdm120ModbusRtuConnection::currentChanged, this, [=](float v) {
+ thing->setStateValue(currentStateTypeId, v);
+ });
+ connect(connection, &Sdm120ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
+ thing->setStateValue(totalEnergyConsumedStateTypeId, v);
+ });
+ connect(connection, &Sdm120ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
+ thing->setStateValue(totalEnergyProducedStateTypeId, v);
+ });
+ } else {
+ // Consumer or producer: connect only the relevant energy direction
+ bool isProducer = (classId == sdm120ProducerThingClassId
+ || classId == sdm220ProducerThingClassId
+ || classId == sdm230ProducerThingClassId);
+ if (isProducer) {
+ connect(connection, &Sdm120ModbusRtuConnection::totalEnergyProducedChanged, this, [=](float v) {
+ thing->setStateValue(totalEnergyProducedStateTypeId, v);
+ });
+ } else {
+ connect(connection, &Sdm120ModbusRtuConnection::totalEnergyConsumedChanged, this, [=](float v) {
+ thing->setStateValue(totalEnergyConsumedStateTypeId, v);
+ });
+ }
+ }
+
+ m_sdm120Connections.insert(thing, connection);
+ info->finish(Thing::ThingErrorNoError);
+}
+
+void IntegrationPluginEastron::postSetupThing(Thing *thing)
+{
+ qCDebug(dcEastron()) << "Post setup thing" << thing->name();
+ if (!m_refreshTimer) {
+ m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
+ connect(m_refreshTimer, &PluginTimer::timeout, this, [this] {
+ foreach (Thing *thing, myThings()) {
+ if (m_sdm630Connections.contains(thing))
+ m_sdm630Connections.value(thing)->update();
+ else if (m_sdm72Connections.contains(thing))
+ m_sdm72Connections.value(thing)->update();
+ else if (m_sdm120Connections.contains(thing))
+ m_sdm120Connections.value(thing)->update();
+ }
+ });
+ qCDebug(dcEastron()) << "Refresh timer started";
+ m_refreshTimer->start();
+ }
+}
+
+void IntegrationPluginEastron::thingRemoved(Thing *thing)
+{
+ qCDebug(dcEastron()) << "Thing removed" << thing->name();
+
+ if (m_sdm630Connections.contains(thing))
+ m_sdm630Connections.take(thing)->deleteLater();
+ else if (m_sdm72Connections.contains(thing))
+ m_sdm72Connections.take(thing)->deleteLater();
+ else if (m_sdm120Connections.contains(thing))
+ m_sdm120Connections.take(thing)->deleteLater();
+
+ if (myThings().isEmpty() && m_refreshTimer) {
+ hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
+ m_refreshTimer = nullptr;
+ qCDebug(dcEastron()) << "Refresh timer stopped";
+ }
+}
diff --git a/eastron/integrationplugineastron.h b/eastron/integrationplugineastron.h
new file mode 100644
index 0000000..a5332b1
--- /dev/null
+++ b/eastron/integrationplugineastron.h
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright (C) 2013 - 2024, nymea GmbH
+* Copyright (C) 2025, ETM-Schurig SARL
+*
+* This file is part of nymea-plugins-modbus.
+*
+* nymea-plugins-modbus is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* nymea-plugins-modbus is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with nymea-plugins-modbus. If not, see .
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef INTEGRATIONPLUGINEASTRON_H
+#define INTEGRATIONPLUGINEASTRON_H
+
+#include
+#include
+#include
+
+#include "sdm630modbusrtuconnection.h"
+#include "sdm72modbusrtuconnection.h"
+#include "sdm120modbusrtuconnection.h"
+
+#include "extern-plugininfo.h"
+
+#include
+#include
+
+class IntegrationPluginEastron: public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugineastron.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ explicit IntegrationPluginEastron();
+ void init() override;
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void setupThing(ThingSetupInfo *info) override;
+ void postSetupThing(Thing *thing) override;
+ void thingRemoved(Thing *thing) override;
+
+private:
+ PluginTimer *m_refreshTimer = nullptr;
+
+ // SDM630 group: sdm630, sdm630Consumer, sdm630Producer
+ QHash m_sdm630Connections;
+ // SDM72 group: sdm72, sdm72Consumer, sdm72Producer
+ QHash m_sdm72Connections;
+ // SDM120 group: sdm120/Consumer/Producer, sdm220/Consumer/Producer, sdm230/Consumer/Producer
+ QHash m_sdm120Connections;
+
+ void discoverRtuDevices(ThingDiscoveryInfo *info, const QString &modelName,
+ const ParamTypeId &discoverySlaveParamTypeId,
+ const ParamTypeId &thingSlaveParamTypeId,
+ const ParamTypeId &thingMasterUuidParamTypeId);
+
+ void setupSdm630(ThingSetupInfo *info);
+ void setupSdm72(ThingSetupInfo *info);
+ void setupSdm120(ThingSetupInfo *info);
+};
+
+#endif // INTEGRATIONPLUGINEASTRON_H
diff --git a/eastron/integrationplugineastron.json b/eastron/integrationplugineastron.json
new file mode 100644
index 0000000..2727fc5
--- /dev/null
+++ b/eastron/integrationplugineastron.json
@@ -0,0 +1,1411 @@
+{
+ "name": "eastron",
+ "displayName": "Eastron",
+ "id": "2078c2fc-c4cd-46bb-95ab-e9a55ef0a281",
+ "paramTypes": [],
+ "vendors": [
+ {
+ "name": "eastron",
+ "displayName": "Eastron",
+ "id": "33529c0d-67e6-441e-b0bb-6e76e9d4bc1c",
+ "thingClasses": [
+
+ {
+ "name": "sdm630",
+ "displayName": "SDM630 — Energy Meter",
+ "id": "0535818b-bd91-4cb8-8ec7-a870c7b12115",
+ "createMethods": ["discovery"],
+ "interfaces": ["energymeter", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "b3ff747c-84a5-4e40-9c45-dd075e767cc3",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "d05fc16a-2fb1-4184-8cd8-2ee05427af13",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "0b1b47fb-2f05-49da-bf55-58d928f02909",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "e7cd3427-e7d9-4859-8f51-7c685e362eec",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "e3196bf6-2933-4166-8e36-518ed456c88b",
+ "name": "voltagePhaseA",
+ "displayName": "Voltage phase A",
+ "displayNameEvent": "Voltage phase A changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "866f9eca-5518-4e0b-8329-fcdef9ff2174",
+ "name": "voltagePhaseB",
+ "displayName": "Voltage phase B",
+ "displayNameEvent": "Voltage phase B changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "3c8e085b-ede9-410b-9e45-84b067616582",
+ "name": "voltagePhaseC",
+ "displayName": "Voltage phase C",
+ "displayNameEvent": "Voltage phase C changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "682e4ed8-a6a4-4303-ab23-6d936bc9ea9b",
+ "name": "currentPhaseA",
+ "displayName": "Current phase A",
+ "displayNameEvent": "Current phase A changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "5b333cb5-2e62-4615-b75c-f931ecdd7ece",
+ "name": "currentPhaseB",
+ "displayName": "Current phase B",
+ "displayNameEvent": "Current phase B changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "e6b7cc83-4fd4-444c-b4e4-098d2e75d06e",
+ "name": "currentPhaseC",
+ "displayName": "Current phase C",
+ "displayNameEvent": "Current phase C changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "a1d63410-a603-4897-b951-15677c773f9d",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "53725aef-3d6e-4c7a-a54a-759f7b15ed7e",
+ "name": "currentPowerPhaseA",
+ "displayName": "Current power phase A",
+ "displayNameEvent": "Current power phase A changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "f739de45-9237-40cc-93d7-283cb7dcb863",
+ "name": "currentPowerPhaseB",
+ "displayName": "Current power phase B",
+ "displayNameEvent": "Current power phase B changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "9629c405-36de-4168-a06b-c6bce48142eb",
+ "name": "currentPowerPhaseC",
+ "displayName": "Current power phase C",
+ "displayNameEvent": "Current power phase C changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "b3724c5a-deda-4186-b364-1f2d65193a80",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ },
+ {
+ "id": "49f6adcd-1994-4686-8294-4afc7a4dcd86",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "f257effd-6c49-4604-b4c1-5689eae312bc",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "df2fbaf0-7e36-4790-b614-6f3d4d990a6c",
+ "name": "energyConsumedPhaseA",
+ "displayName": "Energy consumed phase A",
+ "displayNameEvent": "Energy consumed phase A changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "ef1471f3-37b5-4d4d-9229-6fbb43b73ca3",
+ "name": "energyConsumedPhaseB",
+ "displayName": "Energy consumed phase B",
+ "displayNameEvent": "Energy consumed phase B changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "c04dab09-766d-4868-b284-6edaeb92c498",
+ "name": "energyConsumedPhaseC",
+ "displayName": "Energy consumed phase C",
+ "displayNameEvent": "Energy consumed phase C changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "d0eef54c-6b95-4d8f-be4b-32f7ce983d62",
+ "name": "energyProducedPhaseA",
+ "displayName": "Energy produced phase A",
+ "displayNameEvent": "Energy produced phase A changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "63b65768-3970-4072-b0c2-e2c143b46bef",
+ "name": "energyProducedPhaseB",
+ "displayName": "Energy produced phase B",
+ "displayNameEvent": "Energy produced phase B changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "86d0b64c-488c-4957-b72c-25a7577c41a9",
+ "name": "energyProducedPhaseC",
+ "displayName": "Energy produced phase C",
+ "displayNameEvent": "Energy produced phase C changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm630Consumer",
+ "displayName": "SDM630 — Consumer Meter",
+ "id": "54105387-1d86-409d-abeb-248f78a476b7",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterconsumer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "f1766468-db1c-4cc6-939d-85ff33841659",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "6c2e5662-8c92-4934-88f7-f8a06066a49c",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "58b5d865-a2ef-491d-9c9f-89436ca87011",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "f4decb05-e9de-456f-9b82-f2e86ed41aa9",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "e28e2222-3109-4074-8131-2f5c097e72b0",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "8a204eed-d8ea-49af-85dc-73cd47cd05dd",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "1a66d49e-3da6-4489-bcd7-fe6f384881a0",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm630Producer",
+ "displayName": "SDM630 — Producer Meter",
+ "id": "da785267-1dbe-409a-8892-bb4b3ab68728",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterproducer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "856a0582-c52d-4c37-8173-e0616b529c22",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "c7833946-1e97-4c51-9501-8ff7ca8a0493",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "be7cd84c-3694-4321-9285-853d5ba24e4e",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "935491c9-77dd-4278-a9cb-3788612eab3b",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "74feee50-3469-4365-98d7-c6f5eefe1736",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "4d4247a1-425b-4bee-9b93-1f92447e7779",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "15dd9561-27ad-4a00-a7bc-860ffa17b04f",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm72",
+ "displayName": "SDM72 — Energy Meter",
+ "id": "d058ef0a-0b0d-4248-a39e-21e9397081db",
+ "createMethods": ["discovery"],
+ "interfaces": ["energymeter", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "87b58e0c-3827-4a05-a44c-28f168c65ee3",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "78b2ab1f-19aa-4461-a284-f3b19d99cbe4",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "63405213-b6c3-4b00-ac55-8fba70edb133",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "73328327-a4f3-4214-9af8-751788730a1f",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "c02cdb3c-9069-4fce-9470-277fabc89302",
+ "name": "voltagePhaseA",
+ "displayName": "Voltage phase A",
+ "displayNameEvent": "Voltage phase A changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "53804251-d8fa-4237-8879-2066f31e5e15",
+ "name": "voltagePhaseB",
+ "displayName": "Voltage phase B",
+ "displayNameEvent": "Voltage phase B changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "1301a94a-12e2-4591-8685-4b710c054645",
+ "name": "voltagePhaseC",
+ "displayName": "Voltage phase C",
+ "displayNameEvent": "Voltage phase C changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "ba023b21-3704-4e9a-bad0-ce68415782c9",
+ "name": "currentPhaseA",
+ "displayName": "Current phase A",
+ "displayNameEvent": "Current phase A changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "3eda9880-70cc-4ace-8279-0022de13dcc5",
+ "name": "currentPhaseB",
+ "displayName": "Current phase B",
+ "displayNameEvent": "Current phase B changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "4baeb06f-422a-442a-9d54-b389f555eace",
+ "name": "currentPhaseC",
+ "displayName": "Current phase C",
+ "displayNameEvent": "Current phase C changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "a32b70cf-fd55-4204-971d-865a944c7800",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "15a1d6a7-5d46-4bc9-a83e-db85ddc0b739",
+ "name": "currentPowerPhaseA",
+ "displayName": "Current power phase A",
+ "displayNameEvent": "Current power phase A changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "3b63f4cf-097b-486b-8cf2-97d97ed67478",
+ "name": "currentPowerPhaseB",
+ "displayName": "Current power phase B",
+ "displayNameEvent": "Current power phase B changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "60b42caf-75f6-4f0a-8b33-d59c8a169f37",
+ "name": "currentPowerPhaseC",
+ "displayName": "Current power phase C",
+ "displayNameEvent": "Current power phase C changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "8036f567-2532-4258-8d76-ec2c82bd29f4",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ },
+ {
+ "id": "c6f0c8d6-4bec-445b-843e-baf2f3908d58",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "482757a4-8b8b-4d41-99d9-5ced48459c63",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm72Consumer",
+ "displayName": "SDM72 — Consumer Meter",
+ "id": "e67c2b0f-3905-454f-890a-9cce0800b703",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterconsumer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "7956c051-4e01-4123-abc6-23d7bf1ed05f",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "d7795c92-d00e-49bc-9005-49e1bc520e25",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "4cab7e0c-37bd-44cc-a866-219172535323",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "166471bb-136c-467a-a689-39912be1b7ce",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "3f8dcfbf-9c35-40ed-928f-c8d0859e47f6",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "aa358341-9409-4a99-ab80-4a7196d446c3",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "981e0554-9411-4dd0-8af9-56a54e8a4104",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm72Producer",
+ "displayName": "SDM72 — Producer Meter",
+ "id": "4fe3a9cc-3d37-45eb-989a-63f84fab119e",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterproducer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "1f900a6d-b835-45e6-b236-67362b697e9b",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "8a60ffcd-32d9-4b2d-a525-f3cd22e126bd",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "026481d9-d2de-4082-b5ea-73905afa911e",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "406d6b71-b7d8-481e-93f5-535bbe5dab35",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "a1446da3-3f2d-41a4-a0a8-ebf96bd27b67",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "6a1b0f7c-4829-48dc-85a5-d8bd640bda33",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "bacb8a91-7881-4b41-97ae-dc960c420e4c",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm120",
+ "displayName": "SDM120 — Energy Meter",
+ "id": "46cfdfab-6e93-4568-9ff0-5760909a2a4c",
+ "createMethods": ["discovery"],
+ "interfaces": ["energymeter", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "bf10687e-da5e-4c00-932c-f07b069734f7",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "907e6c11-d334-4a56-a87b-ec3e04ea4904",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "f5c93fbf-325f-4a5e-955d-9b7883c71032",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "956de306-01bb-4f1a-847c-d15e617335a6",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "35f88f7f-a8a0-4090-968c-832905aed291",
+ "name": "voltagePhaseA",
+ "displayName": "Voltage",
+ "displayNameEvent": "Voltage changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "094d3441-1718-4170-a9bd-bb157d554afc",
+ "name": "currentPhaseA",
+ "displayName": "Current",
+ "displayNameEvent": "Current changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "10ea7d4e-e1a4-4999-aca2-b0183bc36fe4",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "4965f4b1-8351-4932-9c3c-776a6e32091b",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ },
+ {
+ "id": "03e259b6-ecca-4ee1-bca6-8f34accd0d28",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "f5348729-8146-4fc1-8d79-fd797e0b4951",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm120Consumer",
+ "displayName": "SDM120 — Consumer Meter",
+ "id": "e519e207-c6af-4479-af02-af36d6dd290e",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterconsumer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "bc0d2184-0d26-4e61-aded-7eecad2ddf11",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "37555aec-b740-4a17-8b49-4c0538330b86",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "7bbd3600-1e72-4dcb-a597-3415d32ea2db",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "112240d5-f6fc-4d67-874e-537bda2fe65c",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "8fc409b7-6e78-4ff5-8910-9b5ad0f59bf0",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "03c78adf-2d57-40a6-835b-492aab9bb021",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "f9b356aa-2468-430b-864d-01ac943dfdc9",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm120Producer",
+ "displayName": "SDM120 — Producer Meter",
+ "id": "162b883b-3010-478d-a373-ee9446a19d15",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterproducer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "28b1ec59-2f1c-4ee9-afd7-60f8b2540b71",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "a93dd24c-50b5-4a74-8929-f54029778dab",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "c358a56b-c7ed-4cf8-b74a-b2c95d2ec978",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "2b00f43c-ad66-4ae0-9981-1a43dfd68fde",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "5c801cb8-b891-45d9-978e-be8ae3a9a6ca",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "04d99792-1c4d-48fb-8ad6-370689ce387c",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "c26381c5-0523-4d13-b818-e4e2144e629a",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm220",
+ "displayName": "SDM220 — Energy Meter",
+ "id": "3cf3b42b-cc14-4f5b-a291-f8682d7f5c9a",
+ "createMethods": ["discovery"],
+ "interfaces": ["energymeter", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "56293d68-2e00-4def-bef0-d6b0f7289144",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "cc6aa752-86da-4155-8430-9eb79fd10d09",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "16cb45f5-7dde-4602-90cd-bbe2d9d58a3c",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "6ff17c3f-9d56-4146-a985-16676732d78b",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "eb047b88-6869-4d32-b9c3-a3654d3ca64b",
+ "name": "voltagePhaseA",
+ "displayName": "Voltage",
+ "displayNameEvent": "Voltage changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "f47d9db8-23b3-4b51-bfde-3ff34c1dedf9",
+ "name": "currentPhaseA",
+ "displayName": "Current",
+ "displayNameEvent": "Current changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "0c057463-d4a7-4deb-af18-ff7f90a11262",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "bc82a8a3-2b04-417c-afac-e3f5494d9985",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ },
+ {
+ "id": "c9716b87-8e1d-4cd0-a526-dd1c220cce1b",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "7f6b35c5-a244-475f-838d-c77aac7b76a1",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm220Consumer",
+ "displayName": "SDM220 — Consumer Meter",
+ "id": "252578a4-513b-46de-b10f-0bd6efb891aa",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterconsumer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "089bd1af-5196-4d7f-9682-16aa2d988f66",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "7919c29b-22ae-49f0-8c4e-81546e7af2e6",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "5066a96f-54c4-4165-85dc-b8cae4e6bfb6",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "6574af2b-7bdc-42a9-90f8-9c6381c692fc",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "4ec350aa-763b-4500-909b-53bd81e0f3cc",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "3de4a1f3-0c5f-454a-8c42-a4eb674fd1ca",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "d610133b-6a5f-4011-84fc-9cdb438312b9",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm220Producer",
+ "displayName": "SDM220 — Producer Meter",
+ "id": "2885a917-c281-4a03-bf98-e5d596eb0288",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterproducer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "dc485bee-2e90-4743-9b73-fbb8b79e1030",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "b7343b02-f937-4fda-8aec-539c989b99c5",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "27c7c43a-087f-49c8-bcfa-0a0988f3fe0f",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "4af34e28-c44a-44da-ba1b-a93b7956a2cc",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "f99063ae-731b-471f-99ba-aa4ca7945a03",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "e5a06165-2bbb-4b6f-8f5f-8e13f6756501",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "1bef1573-8d0f-4ba8-82c9-afdf1437eed4",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm230",
+ "displayName": "SDM230 — Energy Meter",
+ "id": "078d9b32-4004-4ad9-a758-aef99c136858",
+ "createMethods": ["discovery"],
+ "interfaces": ["energymeter", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "39b6cfe3-4ff0-4be1-8d68-f5c34c6c721b",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "56e3270f-5760-46ce-9e15-c5107b287af3",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "768872db-3f33-4d81-abc6-d1fa6d9e2c43",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "2fc9bc20-6f0a-46b3-b1d7-c1837f8a2728",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "3dcdf851-4d47-472e-871b-f69083b10d62",
+ "name": "voltagePhaseA",
+ "displayName": "Voltage",
+ "displayNameEvent": "Voltage changed",
+ "type": "double",
+ "unit": "Volt",
+ "defaultValue": 0
+ },
+ {
+ "id": "6890caf9-6d16-439d-8848-cefb32a16529",
+ "name": "currentPhaseA",
+ "displayName": "Current",
+ "displayNameEvent": "Current changed",
+ "type": "double",
+ "unit": "Ampere",
+ "defaultValue": 0
+ },
+ {
+ "id": "2fb590d5-3b12-4b36-8566-5f9b9b42a669",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "4344b618-51fe-4c04-9e88-0b133c7e4a95",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ },
+ {
+ "id": "41ef34e3-904e-4282-a203-7d3def3b592b",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "e382146d-1734-482a-9fea-ad6158da1268",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm230Consumer",
+ "displayName": "SDM230 — Consumer Meter",
+ "id": "f98df834-6428-4173-a7d1-b91681274bd1",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterconsumer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "ae22d4de-d3f0-4a77-a0bd-8f7070f878fe",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "2fe5a67e-3d9d-4c22-b091-7249df0a1d3a",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "d0c76f54-2439-4d05-b5bf-14a27f28737b",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "d5016371-ef67-4242-8c74-a69e3e985630",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "9ab1167c-f890-47ee-aad2-ab4cae775ae2",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "fdfe8ef0-1f29-4cdd-88d2-165c4adae795",
+ "name": "totalEnergyConsumed",
+ "displayName": "Total energy consumed",
+ "displayNameEvent": "Total energy consumed changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "09877c12-ae5b-413e-b37b-ef104e76b66a",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ },
+
+ {
+ "name": "sdm230Producer",
+ "displayName": "SDM230 — Producer Meter",
+ "id": "3d0c210b-2da9-4dcd-ae3f-aa4318262db3",
+ "createMethods": ["discovery"],
+ "interfaces": ["smartmeterproducer", "connectable"],
+ "discoveryParamTypes": [
+ {
+ "id": "ed1d95cf-0e9f-4529-b514-80b3a57529ac",
+ "name": "slaveAddress",
+ "displayName": "Slave address",
+ "type": "int",
+ "defaultValue": 1
+ }
+ ],
+ "paramTypes": [
+ {
+ "id": "f35d05e5-f968-4a33-87c6-6ce5840f158a",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "uint",
+ "defaultValue": 1
+ },
+ {
+ "id": "402ed4c9-4560-4a74-9c6f-a0b29ea23a32",
+ "name": "modbusMasterUuid",
+ "displayName": "Modbus RTU master",
+ "type": "QUuid",
+ "defaultValue": "",
+ "readOnly": true
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "a964b0df-d4d2-4242-903b-ba654c800591",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "cached": false,
+ "defaultValue": false
+ },
+ {
+ "id": "ae771224-30b7-478b-8b14-d12e0c2d1f70",
+ "name": "currentPower",
+ "displayName": "Current power",
+ "displayNameEvent": "Current power changed",
+ "type": "double",
+ "unit": "Watt",
+ "defaultValue": 0
+ },
+ {
+ "id": "a1f48d01-db15-4883-9a62-9520199771a8",
+ "name": "totalEnergyProduced",
+ "displayName": "Total energy produced",
+ "displayNameEvent": "Total energy produced changed",
+ "type": "double",
+ "unit": "KiloWattHour",
+ "defaultValue": 0
+ },
+ {
+ "id": "d7a9a378-a313-48c5-9360-7ffc8c0a0172",
+ "name": "frequency",
+ "displayName": "Frequency",
+ "displayNameEvent": "Frequency changed",
+ "type": "double",
+ "unit": "Hertz",
+ "defaultValue": 0
+ }
+ ]
+ }
+
+ ]
+ }
+ ]
+}
diff --git a/eastron/meta.json b/eastron/meta.json
new file mode 100644
index 0000000..1b4af0c
--- /dev/null
+++ b/eastron/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "Eastron",
+ "tagline": "Connect Eastron SDM energy meters via Modbus RTU.",
+ "icon": "eastron.jpg",
+ "stability": "consumer",
+ "offline": true,
+ "technologies": [
+ "modbus"
+ ],
+ "categories": [
+ "energy"
+ ]
+}
diff --git a/eastron/sdm120-registers.json b/eastron/sdm120-registers.json
new file mode 100644
index 0000000..3464f86
--- /dev/null
+++ b/eastron/sdm120-registers.json
@@ -0,0 +1,89 @@
+{
+ "className": "Sdm120",
+ "protocol": "RTU",
+ "endianness": "BigEndian",
+ "errorLimitUntilNotReachable": 15,
+ "checkReachableRegister": "activePower",
+ "registers": [
+ {
+ "id": "voltage",
+ "address": 0,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Voltage",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "current",
+ "address": 6,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Current",
+ "unit": "A",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "activePower",
+ "address": 12,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Active power",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ],
+ "blocks": [
+ {
+ "id": "frequencyAndTotalEnergy",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "frequency",
+ "address": 70,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Frequency",
+ "unit": "Hz",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "totalEnergyConsumed",
+ "address": 72,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total import active energy",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "totalEnergyProduced",
+ "address": 74,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total export active energy",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ }
+ ]
+}
diff --git a/eastron/sdm630-registers.json b/eastron/sdm630-registers.json
new file mode 100644
index 0000000..10f6656
--- /dev/null
+++ b/eastron/sdm630-registers.json
@@ -0,0 +1,263 @@
+{
+ "className": "Sdm630",
+ "protocol": "RTU",
+ "endianness": "BigEndian",
+ "errorLimitUntilNotReachable": 15,
+ "checkReachableRegister": "totalCurrentPower",
+ "registers": [
+ {
+ "id": "totalCurrentPower",
+ "address": 52,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total system power",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ],
+ "blocks": [
+ {
+ "id": "phaseVoltageAndCurrent",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "voltagePhaseA",
+ "address": 0,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Voltage phase L1",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "voltagePhaseB",
+ "address": 2,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Voltage phase L2",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "voltagePhaseC",
+ "address": 4,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Voltage phase L3",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseA",
+ "address": 6,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Current phase L1",
+ "unit": "A",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseB",
+ "address": 8,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Current phase L2",
+ "unit": "A",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseC",
+ "address": 10,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Current phase L3",
+ "unit": "A",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ },
+ {
+ "id": "phasePower",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "powerPhaseA",
+ "address": 12,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Power phase L1",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "powerPhaseB",
+ "address": 14,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Power phase L2",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "powerPhaseC",
+ "address": 16,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Power phase L3",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ },
+ {
+ "id": "frequencyAndTotalEnergy",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "frequency",
+ "address": 70,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Frequency",
+ "unit": "Hz",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "totalEnergyConsumed",
+ "address": 72,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total import active energy",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "totalEnergyProduced",
+ "address": 74,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total export active energy",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ },
+ {
+ "id": "phaseEnergy",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "energyProducedPhaseA",
+ "address": 346,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Export active energy phase L1",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "energyProducedPhaseB",
+ "address": 348,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Export active energy phase L2",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "energyProducedPhaseC",
+ "address": 350,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Export active energy phase L3",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "energyConsumedPhaseA",
+ "address": 352,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Import active energy phase L1",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "energyConsumedPhaseB",
+ "address": 354,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Import active energy phase L2",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "energyConsumedPhaseC",
+ "address": 356,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Import active energy phase L3",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ }
+ ]
+}
diff --git a/eastron/sdm72-registers.json b/eastron/sdm72-registers.json
new file mode 100644
index 0000000..346a709
--- /dev/null
+++ b/eastron/sdm72-registers.json
@@ -0,0 +1,185 @@
+{
+ "className": "Sdm72",
+ "protocol": "RTU",
+ "endianness": "BigEndian",
+ "errorLimitUntilNotReachable": 15,
+ "checkReachableRegister": "totalCurrentPower",
+ "registers": [
+ {
+ "id": "totalCurrentPower",
+ "address": 52,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total system power",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ],
+ "blocks": [
+ {
+ "id": "phaseVoltageAndCurrent",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "voltagePhaseA",
+ "address": 0,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Voltage phase L1",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "voltagePhaseB",
+ "address": 2,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Voltage phase L2",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "voltagePhaseC",
+ "address": 4,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Voltage phase L3",
+ "unit": "V",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseA",
+ "address": 6,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Current phase L1",
+ "unit": "A",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseB",
+ "address": 8,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Current phase L2",
+ "unit": "A",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "currentPhaseC",
+ "address": 10,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Current phase L3",
+ "unit": "A",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ },
+ {
+ "id": "phasePower",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "powerPhaseA",
+ "address": 12,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Power phase L1",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "powerPhaseB",
+ "address": 14,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Power phase L2",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "powerPhaseC",
+ "address": 16,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Power phase L3",
+ "unit": "W",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ },
+ {
+ "id": "frequencyAndTotalEnergy",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "frequency",
+ "address": 70,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Frequency",
+ "unit": "Hz",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "totalEnergyConsumed",
+ "address": 72,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total import active energy",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "totalEnergyProduced",
+ "address": 74,
+ "size": 2,
+ "type": "float",
+ "registerType": "inputRegister",
+ "readSchedule": "update",
+ "description": "Total export active energy",
+ "unit": "kWh",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ }
+ ]
+}
diff --git a/modbus.pri b/modbus.pri
new file mode 100644
index 0000000..840bcb4
--- /dev/null
+++ b/modbus.pri
@@ -0,0 +1,12 @@
+QT += network serialport serialbus
+
+top_srcdir=$$PWD
+top_builddir=$$shadowed($$PWD)
+
+INCLUDEPATH += $$top_srcdir/libnymea-modbus
+LIBS += -L$$top_builddir/libnymea-modbus/ -lnymea-modbus
+
+OTHER_FILES += $${MODBUS_CONNECTIONS}
+
+include(libnymea-modbus/modbus-tool.pri)
+
diff --git a/nymea-plugins-modbus.pro b/nymea-plugins-modbus.pro
new file mode 100644
index 0000000..23b985e
--- /dev/null
+++ b/nymea-plugins-modbus.pro
@@ -0,0 +1,70 @@
+TEMPLATE = subdirs
+
+# Note: In the loop at the end of this file the plugin
+# dependency on the libs will be defined
+SUBDIRS += nymea-modbus-cli libnymea-modbus libnymea-sunspec
+
+PLUGIN_DIRS = \
+ eastron-all-models \
+ waveshare-relay-d8 \
+
+
+message(============================================)
+message("Qt version:" $$[QT_VERSION])
+
+!greaterThan(QT_MAJOR_VERSION, 5) {
+ # We disable unipi for
+ PLUGIN_DIRS += \
+ unipi
+}
+
+gcc {
+ COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion")
+ COMPILER_MAJOR_VERSION = $$str_member($$COMPILER_VERSION)
+ greaterThan(COMPILER_MAJOR_VERSION, 7): QMAKE_CXXFLAGS += -Wno-deprecated-copy
+}
+
+plugininfo.depends = FORCE
+for (entry, PLUGIN_DIRS):plugininfo.commands += test -d $${entry} || mkdir -p $${entry}; cd $${entry} && qmake -o Makefile $$PWD/$${entry}/$${entry}.pro && cd ..;
+for (entry, PLUGIN_DIRS):plugininfo.commands += make -C $${entry} plugininfo.h;
+QMAKE_EXTRA_TARGETS += plugininfo
+
+# Translations:
+# make lupdate to update .ts files
+lupdate.depends = FORCE plugininfo
+for (entry, PLUGIN_DIRS):lupdate.commands += make -C $${entry} lupdate;
+QMAKE_EXTRA_TARGETS += lupdate
+
+# make lrelease to build .qm from .ts
+lrelease.depends = FORCE
+for (entry, PLUGIN_DIRS):lrelease.commands += lrelease $$files($$PWD/$${entry}/translations/*.ts, true);
+for (entry, PLUGIN_DIRS):lrelease.commands += rsync -a $$PWD/$${entry}/translations/*.qm $$OUT_PWD/translations/;
+QMAKE_EXTRA_TARGETS += lrelease
+
+# For Qt-Creator's code model: Add CPATH to INCLUDEPATH explicitly
+INCLUDEPATH += $$(CPATH)
+
+message("Usage: qmake [srcdir] [WITH_PLUGINS=\"...\"] [WITHOUT_PLUGINS=\"...\"]")
+
+isEmpty(WITH_PLUGINS) {
+ PLUGINS = $${PLUGIN_DIRS}
+} else {
+ PLUGINS = $${WITH_PLUGINS}
+}
+PLUGINS-=$${WITHOUT_PLUGINS}
+
+message("Building plugins:")
+for(plugin, PLUGINS) {
+ exists($${plugin}) {
+ SUBDIRS*= $${plugin}
+ message("- $${plugin}")
+ # Make sure the libs will be built before the plugins
+ equals(plugin, "sunspec") {
+ $${plugin}.depends += libnymea-sunspec
+ } else {
+ $${plugin}.depends += libnymea-modbus
+ }
+ } else {
+ error("Invalid plugin \"$${plugin}\".")
+ }
+}
diff --git a/plugins.pri b/plugins.pri
new file mode 100644
index 0000000..f180f53
--- /dev/null
+++ b/plugins.pri
@@ -0,0 +1,17 @@
+isEmpty(PLUGIN_PRI) {
+ exists($$[QT_INSTALL_PREFIX]/include/nymea/plugin.pri) {
+ include($$[QT_INSTALL_PREFIX]/include/nymea/plugin.pri)
+ } else {
+ message("plugin.pri not found. Either install libnymea1-dev or use the PLUGIN_PRI argument to point to it.")
+ message("For building this project without nymea installed system-wide, you will want to export those variables in addition:")
+ message("PKG_CONFIG_PATH=/path/to/build-nymea/libnymea/pkgconfig/")
+ message("CPATH=/path/to/nymea/libnymea/")
+ message("LIBRARY_PATH=/path/to/build-nymea/libnymea/")
+ message("PATH=/path/to/build-nymea/tools/nymea-plugininfocompiler:$PATH")
+ message("LD_LIBRARY_PATH=/path/to/build-nymea/libnymea/")
+ error("plugin.pri not found. Cannot continue")
+ }
+} else {
+# message("Using $$PLUGIN_PRI")
+ include($$PLUGIN_PRI)
+}
diff --git a/sunspec.pri b/sunspec.pri
new file mode 100644
index 0000000..d39682d
--- /dev/null
+++ b/sunspec.pri
@@ -0,0 +1,7 @@
+QT *= network serialbus
+
+top_srcdir=$$PWD
+top_builddir=$$shadowed($$PWD)
+
+INCLUDEPATH += $$top_srcdir/libnymea-sunspec
+LIBS += -L$$top_builddir/libnymea-sunspec/ -lnymea-sunspec1
diff --git a/waveshare-relay-d8/integrationpluginwaveshare-relay-d8.json b/waveshare-relay-d8/integrationpluginwaveshare-relay-d8.json
new file mode 100644
index 0000000..95d506e
--- /dev/null
+++ b/waveshare-relay-d8/integrationpluginwaveshare-relay-d8.json
@@ -0,0 +1,223 @@
+{
+ "name": "WaveshareRelayD8",
+ "displayName": "Waveshare Relay D8",
+ "id": "028e7851-a6cc-4480-b174-b94ca638cf56",
+ "vendors": [
+ {
+ "name": "waveshare",
+ "displayName": "Waveshare",
+ "id": "1b021659-70ae-4dbf-b837-f96db1ede396",
+ "thingClasses": [
+ {
+ "name": "waveshareRelayD8",
+ "displayName": "Waveshare Relay D8",
+ "id": "8e8e5643-7dcf-4438-af9b-3d003e0fa0f5",
+ "createMethods": ["discovery"],
+ "interfaces": ["connectable"],
+ "paramTypes": [
+ {
+ "id": "b4a727f1-ca3d-4674-864f-f625debf57e4",
+ "name": "rtuMaster",
+ "displayName": "Modbus RTU master UUID",
+ "type": "QString",
+ "defaultValue": ""
+ },
+ {
+ "id": "ab68c9cd-4785-4b12-8752-b50e9fc60520",
+ "name": "slaveAddress",
+ "displayName": "Modbus slave address",
+ "type": "int",
+ "defaultValue": 1,
+ "minValue": 1,
+ "maxValue": 247
+ }
+ ],
+ "stateTypes": [
+ {
+ "id": "a43070ac-9784-4923-bc7f-16c206b822e1",
+ "name": "connected",
+ "displayName": "Connected",
+ "type": "bool",
+ "defaultValue": false,
+ "cached": false
+ },
+ {
+ "id": "29fcfe96-989d-494d-9717-ce08f992e281",
+ "name": "relay1State",
+ "displayName": "Relay 1",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "ba8e4946-6402-459b-bb92-e72b74294627",
+ "name": "relay2State",
+ "displayName": "Relay 2",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "00904d40-eb47-4165-8776-ee6452bd2cb5",
+ "name": "relay3State",
+ "displayName": "Relay 3",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "111db305-23f5-49e2-8999-ae29cacb13cf",
+ "name": "relay4State",
+ "displayName": "Relay 4",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "83a781a8-c4be-406f-9da3-8c64185a945d",
+ "name": "relay5State",
+ "displayName": "Relay 5",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "526a35bf-9529-40f9-bd50-9865ba7e9776",
+ "name": "relay6State",
+ "displayName": "Relay 6",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "0624aea8-805c-4504-8781-2e64f5d88cfa",
+ "name": "relay7State",
+ "displayName": "Relay 7",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "01c3d958-0de1-4c43-a30c-799fc74244ac",
+ "name": "relay8State",
+ "displayName": "Relay 8",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "d380da7f-00c9-4458-ba4c-ed0d10060a24",
+ "name": "input1State",
+ "displayName": "Input 1",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "1e990bb6-912a-4805-b03a-e41d95ee2494",
+ "name": "input2State",
+ "displayName": "Input 2",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "bb460c57-2690-4ddd-8d0c-efcedc74cc53",
+ "name": "input3State",
+ "displayName": "Input 3",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "0285f664-a46b-478f-b27d-4fd2b171883e",
+ "name": "input4State",
+ "displayName": "Input 4",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "d09da37c-ccb9-4583-a42a-1d5b4e074751",
+ "name": "input5State",
+ "displayName": "Input 5",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "45aca74d-12ed-4fce-a332-22535d91cf87",
+ "name": "input6State",
+ "displayName": "Input 6",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "ef593976-820b-4eb2-9156-8c14e46e9dfe",
+ "name": "input7State",
+ "displayName": "Input 7",
+ "type": "bool",
+ "defaultValue": false
+ },
+ {
+ "id": "6c46fb18-2f34-49c0-b22c-0eb561a9c7b0",
+ "name": "input8State",
+ "displayName": "Input 8",
+ "type": "bool",
+ "defaultValue": false
+ }
+ ],
+ "actionTypes": [
+ {
+ "id": "19526ec5-991d-4b6f-855a-d2b61da05caa",
+ "name": "setRelay",
+ "displayName": "Set relay",
+ "paramTypes": [
+ {
+ "id": "f7b2830a-182c-4de5-b689-9ff599add6ac",
+ "name": "channel",
+ "displayName": "Channel (1-8)",
+ "type": "int",
+ "defaultValue": 1,
+ "minValue": 1,
+ "maxValue": 8
+ },
+ {
+ "id": "dee4196d-586a-4735-8c3e-12e01f849787",
+ "name": "state",
+ "displayName": "State",
+ "type": "bool",
+ "defaultValue": false
+ }
+ ]
+ },
+ {
+ "id": "0c0e9c8f-bf97-4f34-bd31-3252aade9168",
+ "name": "setAllRelays",
+ "displayName": "Set all relays",
+ "paramTypes": [
+ {
+ "id": "791becf8-78aa-4c84-96df-2689d87970c8",
+ "name": "state",
+ "displayName": "State",
+ "type": "bool",
+ "defaultValue": false
+ }
+ ]
+ }
+ ],
+ "eventTypes": [
+ {
+ "id": "63212751-793d-4147-89d0-47d71669c672",
+ "name": "inputChanged",
+ "displayName": "Input changed",
+ "paramTypes": [
+ {
+ "id": "42779413-d416-4211-a1fb-76bcbc90a5cd",
+ "name": "channel",
+ "displayName": "Channel (1-8)",
+ "type": "int",
+ "defaultValue": 1
+ },
+ {
+ "id": "d3f66c2f-ed95-46ae-be06-ce151c1793d3",
+ "name": "state",
+ "displayName": "State",
+ "type": "bool",
+ "defaultValue": false
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/waveshare-relay-d8/integrationpluginwavesharerelayed8.cpp b/waveshare-relay-d8/integrationpluginwavesharerelayed8.cpp
new file mode 100644
index 0000000..d57a77c
--- /dev/null
+++ b/waveshare-relay-d8/integrationpluginwavesharerelayed8.cpp
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright (C) 2025, ETM-Schurig SARL
+*
+* This file is part of nymea-plugins-modbus.
+*
+* nymea-plugins-modbus is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* nymea-plugins-modbus is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with nymea-plugins-modbus. If not, see .
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "integrationpluginwavesharerelayed8.h"
+#include "plugininfo.h"
+#include "hardwaremanager.h"
+#include "hardware/modbus/modbusrtuhardwareresource.h"
+
+IntegrationPluginWaveshareRelayD8::IntegrationPluginWaveshareRelayD8()
+{
+}
+
+void IntegrationPluginWaveshareRelayD8::discoverThings(ThingDiscoveryInfo *info)
+{
+ foreach (ModbusRtuMaster *modbusMaster, hardwareManager()->modbusRtuResource()->modbusRtuMasters()) {
+ qCDebug(dcWaveshareRelayD8()) << "Found RTU master resource" << modbusMaster;
+ if (modbusMaster->connected()
+ && modbusMaster->baudrate() == 9600
+ && modbusMaster->dataBits() == QSerialPort::Data8
+ && modbusMaster->stopBits() == QSerialPort::OneStop
+ && modbusMaster->parity() == QSerialPort::NoParity) {
+ ParamList parameters;
+ ThingDescriptor thingDescriptor(waveshareRelayD8ThingClassId,
+ "Waveshare Relay D8",
+ modbusMaster->serialPort());
+ parameters.append(Param(waveshareRelayD8ThingRtuMasterParamTypeId,
+ modbusMaster->modbusUuid().toString()));
+ thingDescriptor.setParams(parameters);
+ info->addThingDescriptor(thingDescriptor);
+ } else {
+ qCDebug(dcWaveshareRelayD8()) << "Skipping RTU master (not connected or wrong settings):" << modbusMaster;
+ }
+ }
+
+ QString displayMessage;
+ if (info->thingDescriptors().isEmpty()) {
+ displayMessage = QT_TR_NOOP("Please configure a Modbus RTU master with 9600 baud, 8 data bits, 1 stop bit and no parity.");
+ }
+
+ info->finish(Thing::ThingErrorNoError, displayMessage);
+}
+
+void IntegrationPluginWaveshareRelayD8::setupThing(ThingSetupInfo *info)
+{
+ Thing *thing = info->thing();
+ qCDebug(dcWaveshareRelayD8()) << "Setup" << thing << thing->params();
+
+ if (m_connections.contains(thing)) {
+ qCDebug(dcWaveshareRelayD8()) << "Reconfiguring existing thing" << thing->name();
+ m_connections.take(thing)->deleteLater();
+ }
+
+ QUuid rtuMasterUuid = thing->paramValue(waveshareRelayD8ThingRtuMasterParamTypeId).toUuid();
+ ModbusRtuMaster *master = hardwareManager()->modbusRtuResource()->getModbusRtuMaster(rtuMasterUuid);
+ if (!master) {
+ info->finish(Thing::ThingErrorHardwareNotAvailable,
+ QT_TR_NOOP("The Modbus RTU master is not available."));
+ return;
+ }
+
+ quint16 slaveAddress = static_cast(
+ thing->paramValue(waveshareRelayD8ThingSlaveAddressParamTypeId).toUInt());
+
+ WaveshareRelayD8ModbusRtuConnection *connection =
+ new WaveshareRelayD8ModbusRtuConnection(master, slaveAddress, this);
+ connect(info, &ThingSetupInfo::aborted, connection,
+ &WaveshareRelayD8ModbusRtuConnection::deleteLater);
+
+ // Reachability
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::reachableChanged,
+ thing, [connection, thing](bool reachable) {
+ qCDebug(dcWaveshareRelayD8()) << thing->name() << "reachable changed:" << reachable;
+ if (reachable) {
+ connection->initialize();
+ } else {
+ thing->setStateValue(waveshareRelayD8ConnectedStateTypeId, false);
+ }
+ });
+
+ // Initialization
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::initializationFinished,
+ info, [=](bool success) {
+ qCDebug(dcWaveshareRelayD8()) << "Initialization finished:" << success;
+ if (success) {
+ m_connections.insert(thing, connection);
+ info->finish(Thing::ThingErrorNoError);
+ } else {
+ delete connection;
+ info->finish(Thing::ThingErrorHardwareNotAvailable);
+ }
+ });
+
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::initializationFinished,
+ thing, [=](bool success) {
+ if (success) {
+ thing->setStateValue(waveshareRelayD8ConnectedStateTypeId, true);
+ }
+ });
+
+ // Relay state changes (bulk read FC01 → individual signals)
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay1Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay1StateStateTypeId, value != 0);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay2Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay2StateStateTypeId, value != 0);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay3Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay3StateStateTypeId, value != 0);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay4Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay4StateStateTypeId, value != 0);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay5Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay5StateStateTypeId, value != 0);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay6Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay6StateStateTypeId, value != 0);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay7Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay7StateStateTypeId, value != 0);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::relay8Changed,
+ thing, [thing](quint16 value) {
+ thing->setStateValue(waveshareRelayD8Relay8StateStateTypeId, value != 0);
+ });
+
+ // Opto input state changes (bulk read FC02 → individual signals + event)
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input1Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input1StateStateTypeId, state);
+ emitInputChangedEvent(thing, 1, state);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input2Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input2StateStateTypeId, state);
+ emitInputChangedEvent(thing, 2, state);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input3Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input3StateStateTypeId, state);
+ emitInputChangedEvent(thing, 3, state);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input4Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input4StateStateTypeId, state);
+ emitInputChangedEvent(thing, 4, state);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input5Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input5StateStateTypeId, state);
+ emitInputChangedEvent(thing, 5, state);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input6Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input6StateStateTypeId, state);
+ emitInputChangedEvent(thing, 6, state);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input7Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input7StateStateTypeId, state);
+ emitInputChangedEvent(thing, 7, state);
+ });
+ connect(connection, &WaveshareRelayD8ModbusRtuConnection::input8Changed,
+ thing, [this, thing](quint16 value) {
+ bool state = value != 0;
+ thing->setStateValue(waveshareRelayD8Input8StateStateTypeId, state);
+ emitInputChangedEvent(thing, 8, state);
+ });
+
+ connection->update();
+}
+
+void IntegrationPluginWaveshareRelayD8::postSetupThing(Thing *thing)
+{
+ Q_UNUSED(thing)
+ if (!m_pluginTimer) {
+ qCDebug(dcWaveshareRelayD8()) << "Starting plugin timer (5s)...";
+ m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
+ connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
+ foreach (WaveshareRelayD8ModbusRtuConnection *connection, m_connections) {
+ if (connection->reachable()) {
+ connection->update();
+ }
+ }
+ });
+ m_pluginTimer->start();
+ }
+}
+
+void IntegrationPluginWaveshareRelayD8::thingRemoved(Thing *thing)
+{
+ WaveshareRelayD8ModbusRtuConnection *connection = m_connections.take(thing);
+ delete connection;
+
+ if (myThings().isEmpty() && m_pluginTimer) {
+ hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+ m_pluginTimer = nullptr;
+ }
+}
+
+void IntegrationPluginWaveshareRelayD8::executeAction(ThingActionInfo *info)
+{
+ Thing *thing = info->thing();
+ Action action = info->action();
+ WaveshareRelayD8ModbusRtuConnection *connection = m_connections.value(thing);
+
+ if (!connection || !connection->reachable()) {
+ info->finish(Thing::ThingErrorHardwareNotAvailable);
+ return;
+ }
+
+ if (action.actionTypeId() == waveshareRelayD8SetRelayActionTypeId) {
+ int channel = action.paramValue(waveshareRelayD8SetRelayActionChannelParamTypeId).toInt();
+ quint16 value = action.paramValue(waveshareRelayD8SetRelayActionStateParamTypeId).toBool() ? 1 : 0;
+
+ ModbusRtuReply *reply = nullptr;
+ switch (channel) {
+ case 1: reply = connection->setRelay1(value); break;
+ case 2: reply = connection->setRelay2(value); break;
+ case 3: reply = connection->setRelay3(value); break;
+ case 4: reply = connection->setRelay4(value); break;
+ case 5: reply = connection->setRelay5(value); break;
+ case 6: reply = connection->setRelay6(value); break;
+ case 7: reply = connection->setRelay7(value); break;
+ case 8: reply = connection->setRelay8(value); break;
+ default:
+ qCWarning(dcWaveshareRelayD8()) << "Invalid channel:" << channel;
+ info->finish(Thing::ThingErrorInvalidParameter);
+ return;
+ }
+
+ if (!reply) {
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ connect(reply, &ModbusRtuReply::finished, info, [reply, info]() {
+ if (reply->error() == ModbusRtuReply::NoError) {
+ info->finish(Thing::ThingErrorNoError);
+ } else {
+ qCWarning(dcWaveshareRelayD8()) << "setRelay write error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareFailure);
+ }
+ });
+
+ } else if (action.actionTypeId() == waveshareRelayD8SetAllRelaysActionTypeId) {
+ quint16 value = action.paramValue(waveshareRelayD8SetAllRelaysActionStateParamTypeId).toBool() ? 1 : 0;
+ QVector values(8, value);
+
+ // FC0F: write multiple coils at once (addresses 0-7)
+ ModbusRtuReply *reply = connection->modbusRtuMaster()->writeCoils(
+ connection->slaveId(), 0, values);
+
+ if (!reply) {
+ info->finish(Thing::ThingErrorHardwareFailure);
+ return;
+ }
+
+ connect(reply, &ModbusRtuReply::finished, info, [reply, info]() {
+ if (reply->error() == ModbusRtuReply::NoError) {
+ info->finish(Thing::ThingErrorNoError);
+ } else {
+ qCWarning(dcWaveshareRelayD8()) << "setAllRelays write error:" << reply->errorString();
+ info->finish(Thing::ThingErrorHardwareFailure);
+ }
+ });
+
+ } else {
+ info->finish(Thing::ThingErrorActionTypeNotFound);
+ }
+}
+
+void IntegrationPluginWaveshareRelayD8::emitInputChangedEvent(Thing *thing, int channel, bool state)
+{
+ Event event;
+ event.setEventTypeId(waveshareRelayD8InputChangedEventTypeId);
+ event.setThingId(thing->id());
+ ParamList params;
+ params << Param(waveshareRelayD8InputChangedEventChannelParamTypeId, channel);
+ params << Param(waveshareRelayD8InputChangedEventStateParamTypeId, state);
+ event.setParams(params);
+ emit emitEvent(event);
+}
diff --git a/waveshare-relay-d8/integrationpluginwavesharerelayed8.h b/waveshare-relay-d8/integrationpluginwavesharerelayed8.h
new file mode 100644
index 0000000..50fdebe
--- /dev/null
+++ b/waveshare-relay-d8/integrationpluginwavesharerelayed8.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright (C) 2025, ETM-Schurig SARL
+*
+* This file is part of nymea-plugins-modbus.
+*
+* nymea-plugins-modbus is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* nymea-plugins-modbus is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with nymea-plugins-modbus. If not, see .
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef INTEGRATIONPLUGINWAVESHARERELAYED8_H
+#define INTEGRATIONPLUGINWAVESHARERELAYED8_H
+
+#include
+#include
+
+#include "extern-plugininfo.h"
+#include "autogenerated/wavesharerelayd8modbusrtuconnection.h"
+
+class IntegrationPluginWaveshareRelayD8 : public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginwaveshare-relay-d8.json")
+ Q_INTERFACES(IntegrationPlugin)
+
+public:
+ explicit IntegrationPluginWaveshareRelayD8();
+
+ void discoverThings(ThingDiscoveryInfo *info) override;
+ void setupThing(ThingSetupInfo *info) override;
+ void postSetupThing(Thing *thing) override;
+ void thingRemoved(Thing *thing) override;
+ void executeAction(ThingActionInfo *info) override;
+
+private:
+ PluginTimer *m_pluginTimer = nullptr;
+ QHash m_connections;
+
+ void emitInputChangedEvent(Thing *thing, int channel, bool state);
+};
+
+#endif // INTEGRATIONPLUGINWAVESHARERELAYED8_H
diff --git a/waveshare-relay-d8/meta.json b/waveshare-relay-d8/meta.json
new file mode 100644
index 0000000..d5b6077
--- /dev/null
+++ b/waveshare-relay-d8/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": "Waveshare Relay D8",
+ "tagline": "8-channel Modbus RTU relay module with opto-isolated inputs",
+ "icon": "waveshare.png",
+ "stability": "experimental",
+ "offline": true,
+ "technologies": [
+ "modbus"
+ ],
+ "categories": [
+ "actor"
+ ]
+}
diff --git a/waveshare-relay-d8/waveshare-relay-d8-registers.json b/waveshare-relay-d8/waveshare-relay-d8-registers.json
new file mode 100644
index 0000000..f8e938f
--- /dev/null
+++ b/waveshare-relay-d8/waveshare-relay-d8-registers.json
@@ -0,0 +1,182 @@
+{
+ "className": "WaveshareRelayD8",
+ "protocol": "RTU",
+ "endianness": "BigEndian",
+ "errorLimitUntilNotReachable": 10,
+ "checkReachableRegister": "relay1",
+ "registers": [],
+ "blocks": [
+ {
+ "id": "relayStates",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "relay1",
+ "address": 0,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 1",
+ "defaultValue": "0",
+ "access": "RW"
+ },
+ {
+ "id": "relay2",
+ "address": 1,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 2",
+ "defaultValue": "0",
+ "access": "RW"
+ },
+ {
+ "id": "relay3",
+ "address": 2,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 3",
+ "defaultValue": "0",
+ "access": "RW"
+ },
+ {
+ "id": "relay4",
+ "address": 3,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 4",
+ "defaultValue": "0",
+ "access": "RW"
+ },
+ {
+ "id": "relay5",
+ "address": 4,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 5",
+ "defaultValue": "0",
+ "access": "RW"
+ },
+ {
+ "id": "relay6",
+ "address": 5,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 6",
+ "defaultValue": "0",
+ "access": "RW"
+ },
+ {
+ "id": "relay7",
+ "address": 6,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 7",
+ "defaultValue": "0",
+ "access": "RW"
+ },
+ {
+ "id": "relay8",
+ "address": 7,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "coils",
+ "description": "Relay 8",
+ "defaultValue": "0",
+ "access": "RW"
+ }
+ ]
+ },
+ {
+ "id": "inputStates",
+ "readSchedule": "update",
+ "registers": [
+ {
+ "id": "input1",
+ "address": 0,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 1",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "input2",
+ "address": 1,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 2",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "input3",
+ "address": 2,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 3",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "input4",
+ "address": 3,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 4",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "input5",
+ "address": 4,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 5",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "input6",
+ "address": 5,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 6",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "input7",
+ "address": 6,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 7",
+ "defaultValue": "0",
+ "access": "RO"
+ },
+ {
+ "id": "input8",
+ "address": 7,
+ "size": 1,
+ "type": "uint16",
+ "registerType": "discreteInputs",
+ "description": "Opto input 8",
+ "defaultValue": "0",
+ "access": "RO"
+ }
+ ]
+ }
+ ]
+}
diff --git a/waveshare-relay-d8/waveshare-relay-d8.pro b/waveshare-relay-d8/waveshare-relay-d8.pro
new file mode 100644
index 0000000..eca5922
--- /dev/null
+++ b/waveshare-relay-d8/waveshare-relay-d8.pro
@@ -0,0 +1,13 @@
+include(../plugins.pri)
+
+# Generate modbus connection
+MODBUS_CONNECTIONS += waveshare-relay-d8-registers.json
+MODBUS_TOOLS_CONFIG += VERBOSE
+
+include(../modbus.pri)
+
+HEADERS += \
+ integrationpluginwavesharerelayed8.h
+
+SOURCES += \
+ integrationpluginwavesharerelayed8.cpp