From 40d7457c82187e054a5b30c37041282cb51a4a1f Mon Sep 17 00:00:00 2001 From: Brian <165865819+brianshea2@users.noreply.github.com> Date: Mon, 6 May 2024 18:47:19 -0400 Subject: [PATCH] initial public commit --- .gitignore | 2 + COPYRIGHT | 14 + Dockerfile.meshobserv | 11 + LICENSE | 661 +++++++++++++++++++++++++++++ README.md | 2 + cmd/meshobserv/meshobserv.go | 221 ++++++++++ configs/nginx-http.conf | 12 + go.mod | 1 + internal/meshtastic/mqtt.go | 131 ++++++ internal/meshtastic/node.go | 224 ++++++++++ patch/remove-nanopb.patch | 26 ++ scripts/build-meshobserv.sh | 10 + scripts/generate-pb.sh | 16 + scripts/run-meshobserv.sh | 10 + website/android-chrome-192x192.png | Bin 0 -> 16022 bytes website/android-chrome-512x512.png | Bin 0 -> 51355 bytes website/apple-touch-icon.png | Bin 0 -> 14470 bytes website/favicon.ico | Bin 0 -> 15406 bytes website/index.html | 353 +++++++++++++++ website/site.webmanifest | 17 + 20 files changed, 1711 insertions(+) create mode 100644 .gitignore create mode 100644 COPYRIGHT create mode 100644 Dockerfile.meshobserv create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmd/meshobserv/meshobserv.go create mode 100644 configs/nginx-http.conf create mode 100644 go.mod create mode 100644 internal/meshtastic/mqtt.go create mode 100644 internal/meshtastic/node.go create mode 100644 patch/remove-nanopb.patch create mode 100755 scripts/build-meshobserv.sh create mode 100755 scripts/generate-pb.sh create mode 100755 scripts/run-meshobserv.sh create mode 100644 website/android-chrome-192x192.png create mode 100644 website/android-chrome-512x512.png create mode 100644 website/apple-touch-icon.png create mode 100644 website/favicon.ico create mode 100644 website/index.html create mode 100644 website/site.webmanifest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb84b46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +internal/meshtastic/generated/* +website/nodes.json diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..9b10a2f --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,14 @@ +Copyright (C) 2024 Brian Shea + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/Dockerfile.meshobserv b/Dockerfile.meshobserv new file mode 100644 index 0000000..9ba37bf --- /dev/null +++ b/Dockerfile.meshobserv @@ -0,0 +1,11 @@ +FROM golang:latest AS build +WORKDIR /build +COPY go.mod ./ +COPY cmd/meshobserv ./ +COPY internal ./internal/ +RUN go mod tidy +RUN go build -v -o meshobserv + +FROM debian:bookworm-slim +COPY --from=build /build/meshobserv /usr/bin/ +ENTRYPOINT ["/usr/bin/meshobserv"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..cef60d6 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# [MeshMap.net](https://meshmap.net/) +A nearly live map of Meshtastic nodes seen by the official Meshtastic MQTT server diff --git a/cmd/meshobserv/meshobserv.go b/cmd/meshobserv/meshobserv.go new file mode 100644 index 0000000..f2e3ec4 --- /dev/null +++ b/cmd/meshobserv/meshobserv.go @@ -0,0 +1,221 @@ +package main + +import ( + "errors" + "flag" + "io/fs" + "log" + "os" + "os/signal" + "regexp" + "sync" + "sync/atomic" + "syscall" + "time" + + "github.com/brianshea2/meshmap.net/internal/meshtastic" + "github.com/brianshea2/meshmap.net/internal/meshtastic/generated" + "google.golang.org/protobuf/proto" +) + +const ( + NodeExpiration = 172800 // 2 days + PruneWriteInterval = time.Minute +) + +var ( + Nodes meshtastic.NodeDB + NodesMutex sync.Mutex + Receiving atomic.Bool +) + +func handleMessage(from uint32, topic string, portNum generated.PortNum, payload []byte) { + Receiving.Store(true) + switch portNum { + case generated.PortNum_TEXT_MESSAGE_APP: + log.Printf("[msg] %v (%v) %s: \"%s\"", from, topic, portNum, payload) + case generated.PortNum_POSITION_APP: + var position generated.Position + if err := proto.Unmarshal(payload, &position); err != nil { + log.Printf("[warn] could not parse Position payload from %v on %v: %v", from, topic, err) + return + } + latitude := position.GetLatitudeI() + longitude := position.GetLongitudeI() + precision := position.GetPrecisionBits() + log.Printf("[msg] %v (%v) %s: (%v, %v) %v/32", from, topic, portNum, latitude, longitude, precision) + if latitude == 0 && longitude == 0 { + return + } + NodesMutex.Lock() + if Nodes[from] == nil { + Nodes[from] = meshtastic.NewNode(topic) + } + Nodes[from].UpdatePosition(latitude, longitude, precision) + Nodes[from].UpdateSeenBy(topic) + NodesMutex.Unlock() + case generated.PortNum_NODEINFO_APP: + var user generated.User + if err := proto.Unmarshal(payload, &user); err != nil { + log.Printf("[warn] could not parse User payload from %v on %v: %v", from, topic, err) + return + } + longName := user.GetLongName() + shortName := user.GetShortName() + hwModel := user.GetHwModel().String() + role := user.GetRole().String() + log.Printf("[msg] %v (%v) %s: {\"%v\" \"%v\" %v %v}", from, topic, portNum, longName, shortName, hwModel, role) + if len(longName) == 0 { + return + } + NodesMutex.Lock() + if Nodes[from] == nil { + Nodes[from] = meshtastic.NewNode(topic) + } + Nodes[from].UpdateUser(longName, shortName, hwModel, role) + NodesMutex.Unlock() + case generated.PortNum_TELEMETRY_APP: + var telemetry generated.Telemetry + if err := proto.Unmarshal(payload, &telemetry); err != nil { + log.Printf("[warn] could not parse Telemetry payload from %v on %v: %v", from, topic, err) + return + } + if deviceMetrics := telemetry.GetDeviceMetrics(); deviceMetrics != nil { + batteryLevel := deviceMetrics.GetBatteryLevel() + voltage := deviceMetrics.GetVoltage() + chUtil := deviceMetrics.GetChannelUtilization() + airUtilTx := deviceMetrics.GetAirUtilTx() + uptime := deviceMetrics.GetUptimeSeconds() + log.Printf( + "[msg] %v (%v) %s: DeviceMetrics{power: %v%% (%vV); chUtil: %v%%; airUtilTx: %v%%; uptime: %vs}", + from, topic, portNum, batteryLevel, voltage, chUtil, airUtilTx, uptime, + ) + NodesMutex.Lock() + if Nodes[from] == nil { + Nodes[from] = meshtastic.NewNode(topic) + } + Nodes[from].UpdateDeviceMetrics(batteryLevel, voltage, chUtil, airUtilTx, uptime) + NodesMutex.Unlock() + } + case generated.PortNum_NEIGHBORINFO_APP: + var neighborInfo generated.NeighborInfo + if err := proto.Unmarshal(payload, &neighborInfo); err != nil { + log.Printf("[warn] could not parse NeighborInfo payload from %v on %v: %v", from, topic, err) + return + } + nodeNum := neighborInfo.GetNodeId() + neighbors := neighborInfo.GetNeighbors() + log.Printf("[msg] %v (%v) %s: %v <-> %v neighbors", from, topic, portNum, nodeNum, len(neighbors)) + if nodeNum != from { + return + } + if len(neighbors) == 0 { + return + } + NodesMutex.Lock() + if Nodes[from] == nil { + Nodes[from] = meshtastic.NewNode(topic) + } + for _, neighbor := range neighbors { + neighborNum := neighbor.GetNodeId() + if neighborNum == 0 { + continue + } + Nodes[from].UpdateNeighborInfo(neighborNum, neighbor.GetSnr()) + } + NodesMutex.Unlock() + case generated.PortNum_MAP_REPORT_APP: + var mapReport generated.MapReport + if err := proto.Unmarshal(payload, &mapReport); err != nil { + log.Printf("[warn] could not parse MapReport payload from %v on %v: %v", from, topic, err) + return + } + longName := mapReport.GetLongName() + shortName := mapReport.GetShortName() + hwModel := mapReport.GetHwModel().String() + role := mapReport.GetRole().String() + fwVersion := mapReport.GetFirmwareVersion() + region := mapReport.GetRegion().String() + modemPreset := mapReport.GetModemPreset().String() + hasDefaultCh := mapReport.GetHasDefaultChannel() + latitude := mapReport.GetLatitudeI() + longitude := mapReport.GetLongitudeI() + precision := mapReport.GetPositionPrecision() + log.Printf( + "[msg] %v (%v) %s: {\"%v\" \"%v\" %v %v %v %v %v %v} (%v, %v) %v/32", + from, topic, portNum, + longName, shortName, hwModel, role, fwVersion, region, modemPreset, hasDefaultCh, + latitude, longitude, precision, + ) + if len(longName) == 0 { + return + } + if latitude == 0 && longitude == 0 { + return + } + NodesMutex.Lock() + if Nodes[from] == nil { + Nodes[from] = meshtastic.NewNode(topic) + } + Nodes[from].UpdateUser(longName, shortName, hwModel, role) + Nodes[from].UpdateMapReport(fwVersion, region, modemPreset, hasDefaultCh) + Nodes[from].UpdatePosition(latitude, longitude, precision) + Nodes[from].UpdateSeenBy(topic) + NodesMutex.Unlock() + default: + log.Printf("[msg] %v (%v) %s", from, topic, portNum) + } +} + +func main() { + var dbPath string + flag.StringVar(&dbPath, "f", "", "node database `file`") + flag.Parse() + // load or make NodeDB + if len(dbPath) > 0 { + err := Nodes.LoadFile(dbPath) + if err != nil && !errors.Is(err, fs.ErrNotExist) { + log.Fatalf("[error] load nodes: %v", err) + } + log.Printf("[info] loaded %v nodes from disk", len(Nodes)) + } + if Nodes == nil { + Nodes = make(meshtastic.NodeDB) + } + // connect to MQTT + client := &meshtastic.MQTTClient{ + TopicRegex: regexp.MustCompile(`/2/[ce]/[^/]+/![0-9a-f]+$|/2/map/$`), + BlockCipher: meshtastic.NewBlockCipher(meshtastic.DefaultKey), + MessageHandler: handleMessage, + } + err := client.Connect() + if err != nil { + log.Fatalf("[error] connect: %v", err) + } + // start NodeDB prune and write loop + go func() { + for { + time.Sleep(PruneWriteInterval) + NodesMutex.Lock() + Nodes.Prune(NodeExpiration, NodeExpiration, NodeExpiration, NodeExpiration) + if len(dbPath) > 0 { + valid := Nodes.GetValid() + err := valid.WriteFile(dbPath) + if err != nil { + log.Fatalf("[error] write nodes: %v", err) + } + log.Printf("[info] wrote %v nodes to disk", len(valid)) + } + NodesMutex.Unlock() + if !Receiving.CompareAndSwap(true, false) { + log.Fatal("[crit] no messages received") + } + } + }() + // wait until exit + terminate := make(chan os.Signal, 1) + signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM) + <-terminate + log.Print("[info] exiting") + client.Disconnect() +} diff --git a/configs/nginx-http.conf b/configs/nginx-http.conf new file mode 100644 index 0000000..051d77d --- /dev/null +++ b/configs/nginx-http.conf @@ -0,0 +1,12 @@ +server { + listen 80; + listen [::]:80; + server_name meshmap.net; + root /data/meshmap.net/website; + location / { + index index.html; + try_files $uri $uri/ =404; + add_header Cache-Control "public, max-age=60"; + add_header Vary "Accept-Encoding"; + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a8aad77 --- /dev/null +++ b/go.mod @@ -0,0 +1 @@ +module github.com/brianshea2/meshmap.net diff --git a/internal/meshtastic/mqtt.go b/internal/meshtastic/mqtt.go new file mode 100644 index 0000000..3bde51c --- /dev/null +++ b/internal/meshtastic/mqtt.go @@ -0,0 +1,131 @@ +package meshtastic + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/binary" + "fmt" + "log" + "os" + "regexp" + "time" + + "github.com/brianshea2/meshmap.net/internal/meshtastic/generated" + mqtt "github.com/eclipse/paho.mqtt.golang" + "google.golang.org/protobuf/proto" +) + +const RootTopic = "msh/#" + +var DefaultKey = []byte{ + 0xd4, 0xf1, 0xbb, 0x3a, + 0x20, 0x29, 0x07, 0x59, + 0xf0, 0xbc, 0xff, 0xab, + 0xcf, 0x4e, 0x69, 0x01, +} + +func NewBlockCipher(key []byte) cipher.Block { + c, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + return c +} + +type MQTTClient struct { + TopicRegex *regexp.Regexp + BlockCipher cipher.Block + MessageHandler func(from uint32, topic string, portNum generated.PortNum, payload []byte) + mqtt.Client +} + +func (c *MQTTClient) Connect() error { + randomId := make([]byte, 4) + rand.Read(randomId) + opts := mqtt.NewClientOptions() + opts.AddBroker("tcp://mqtt.meshtastic.org:1883") + opts.SetClientID(fmt.Sprintf("meshobserv-%x", randomId)) + opts.SetUsername("meshdev") + opts.SetPassword("large4cats") + opts.SetOrderMatters(false) + opts.SetDefaultPublishHandler(c.handleMessage) + c.Client = mqtt.NewClient(opts) + token := c.Client.Connect() + <-token.Done() + if err := token.Error(); err != nil { + return err + } + log.Print("[info] connected") + token = c.Subscribe(RootTopic, 0, nil) + <-token.Done() + if err := token.Error(); err != nil { + return err + } + log.Print("[info] subscribed") + return nil +} + +func (c *MQTTClient) Disconnect() { + if c.IsConnected() { + if c.Unsubscribe(RootTopic).WaitTimeout(time.Second) { + log.Print("[info] unsubscribed") + } + c.Client.Disconnect(1000) + } +} + +func (c *MQTTClient) handleMessage(_ mqtt.Client, msg mqtt.Message) { + // filter topic + topic := msg.Topic() + if !c.TopicRegex.MatchString(topic) { + return + } + // parse ServiceEnvelope + var envelope generated.ServiceEnvelope + if err := proto.Unmarshal(msg.Payload(), &envelope); err != nil { + log.Printf("[warn] could not parse ServiceEnvelope on %v: %v", topic, err) + return + } + // get MeshPacket + packet := envelope.GetPacket() + if packet == nil { + log.Printf("[warn] skipping ServiceEnvelope with no MeshPacket on %v", topic) + return + } + // no anonymous packets + from := packet.GetFrom() + if from == 0 { + log.Printf("[warn] skipping MeshPacket from unknown on %v", topic) + return + } + // get Data, try decoded first + data := packet.GetDecoded() + if data == nil { + // data must be (probably) encrypted + encrypted := packet.GetEncrypted() + if encrypted == nil { + log.Printf("[warn] skipping MeshPacket from %v with no data on %v", from, topic) + return + } + // decrypt + nonce := make([]byte, 16) + binary.LittleEndian.PutUint32(nonce[0:], packet.GetId()) + binary.LittleEndian.PutUint32(nonce[8:], from) + decrypted := make([]byte, len(encrypted)) + cipher.NewCTR(c.BlockCipher, nonce).XORKeyStream(decrypted, encrypted) + // parse Data + data = new(generated.Data) + if err := proto.Unmarshal(decrypted, data); err != nil { + // ignore, probably encrypted with other psk + return + } + } + c.MessageHandler(from, topic, data.GetPortnum(), data.GetPayload()) +} + +func init() { + mqtt.ERROR = log.New(os.Stderr, "[error] mqtt: ", log.Flags()|log.Lmsgprefix) + mqtt.CRITICAL = log.New(os.Stderr, "[crit] mqtt: ", log.Flags()|log.Lmsgprefix) + mqtt.WARN = log.New(os.Stderr, "[warn] mqtt: ", log.Flags()|log.Lmsgprefix) +} diff --git a/internal/meshtastic/node.go b/internal/meshtastic/node.go new file mode 100644 index 0000000..505a5f2 --- /dev/null +++ b/internal/meshtastic/node.go @@ -0,0 +1,224 @@ +package meshtastic + +import ( + "encoding/json" + "os" + "path/filepath" + "time" +) + +const ( + SeenByLimit = 10 + NeighborLimit = 100 +) + +type NeighborInfo struct { + Snr float32 `json:"snr,omitempty"` + Updated int64 `json:"updated"` +} + +type Node struct { + // User + LongName string `json:"longName"` + ShortName string `json:"shortName"` + HwModel string `json:"hwModel"` + Role string `json:"role"` + // MapReport + FwVersion string `json:"fwVersion,omitempty"` + Region string `json:"region,omitempty"` + ModemPreset string `json:"modemPreset,omitempty"` + HasDefaultCh bool `json:"hasDefaultCh,omitempty"` + LastMapReport int64 `json:"lastMapReport,omitempty"` + // Position + Latitude int32 `json:"latitude"` + Longitude int32 `json:"longitude"` + Precision uint32 `json:"precision,omitempty"` + // DeviceMetrics + BatteryLevel uint32 `json:"batteryLevel,omitempty"` + Voltage float32 `json:"voltage,omitempty"` + ChUtil float32 `json:"chUtil,omitempty"` + AirUtilTx float32 `json:"airUtilTx,omitempty"` + Uptime uint32 `json:"uptime,omitempty"` + LastDeviceMetrics int64 `json:"lastDeviceMetrics,omitempty"` + // NeighborInfo + Neighbors map[uint32]*NeighborInfo `json:"neighbors,omitempty"` + // key=mqtt topic, value=first seen/last position update + SeenBy map[string]int64 `json:"seenBy"` +} + +func NewNode(topic string) *Node { + return &Node{ + SeenBy: map[string]int64{topic: time.Now().Unix()}, + } +} + +func (node *Node) ClearDeviceMetrics() { + node.BatteryLevel = 0 + node.Voltage = 0 + node.ChUtil = 0 + node.AirUtilTx = 0 + node.Uptime = 0 + node.LastDeviceMetrics = 0 +} + +func (node *Node) ClearMapReportData() { + node.FwVersion = "" + node.Region = "" + node.ModemPreset = "" + node.HasDefaultCh = false + node.LastMapReport = 0 +} + +func (node *Node) IsValid() bool { + if len(node.SeenBy) == 0 { + return false + } + if len(node.LongName) == 0 { + return false + } + if node.Latitude == 0 && node.Longitude == 0 { + return false + } + return true +} + +func (node *Node) Prune(seenByTtl, neighborTtl, deviceMetricsTtl, mapReportTtl int64) { + now := time.Now().Unix() + // SeenBy + for topic, lastSeen := range node.SeenBy { + if lastSeen+seenByTtl < now { + delete(node.SeenBy, topic) + } + } + for len(node.SeenBy) > SeenByLimit { + var toDelete string + for topic, lastSeen := range node.SeenBy { + if len(toDelete) == 0 || lastSeen < node.SeenBy[toDelete] { + toDelete = topic + } + } + delete(node.SeenBy, toDelete) + } + // Neighbors + for neighborNum, neighbor := range node.Neighbors { + if neighbor.Updated+neighborTtl < now { + delete(node.Neighbors, neighborNum) + } + } + if len(node.Neighbors) == 0 { + node.Neighbors = nil + } + for len(node.Neighbors) > NeighborLimit { + var toDelete uint32 + for neighborNum, neighbor := range node.Neighbors { + if toDelete == 0 || neighbor.Updated < node.Neighbors[toDelete].Updated { + toDelete = neighborNum + } + } + delete(node.Neighbors, toDelete) + } + // DeviceMetrics + if node.LastDeviceMetrics > 0 && node.LastDeviceMetrics+deviceMetricsTtl < now { + node.ClearDeviceMetrics() + } + // MapReport + if node.LastMapReport > 0 && node.LastMapReport+mapReportTtl < now { + node.ClearMapReportData() + } +} + +func (node *Node) UpdateDeviceMetrics(batteryLevel uint32, voltage, chUtil, airUtilTx float32, uptime uint32) { + node.BatteryLevel = batteryLevel + node.Voltage = voltage + node.ChUtil = chUtil + node.AirUtilTx = airUtilTx + node.Uptime = uptime + node.LastDeviceMetrics = time.Now().Unix() +} + +func (node *Node) UpdateMapReport(fwVersion, region, modemPreset string, hasDefaultCh bool) { + node.FwVersion = fwVersion + node.Region = region + node.ModemPreset = modemPreset + node.HasDefaultCh = hasDefaultCh + node.LastMapReport = time.Now().Unix() +} + +func (node *Node) UpdateNeighborInfo(neighborNum uint32, snr float32) { + if node.Neighbors == nil { + node.Neighbors = make(map[uint32]*NeighborInfo) + } + node.Neighbors[neighborNum] = &NeighborInfo{ + Snr: snr, + Updated: time.Now().Unix(), + } +} + +func (node *Node) UpdatePosition(latitude, longitude int32, precision uint32) { + node.Latitude = latitude + node.Longitude = longitude + node.Precision = precision +} + +func (node *Node) UpdateSeenBy(topic string) { + node.SeenBy[topic] = time.Now().Unix() +} + +func (node *Node) UpdateUser(longName, shortName, hwModel, role string) { + node.LongName = longName + node.ShortName = shortName + node.HwModel = hwModel + node.Role = role +} + +type NodeDB map[uint32]*Node + +func (db NodeDB) Prune(seenByTtl, neighborTtl, deviceMetricsTtl, mapReportTtl int64) { + for nodeNum, node := range db { + node.Prune(seenByTtl, neighborTtl, deviceMetricsTtl, mapReportTtl) + if len(node.SeenBy) == 0 { + delete(db, nodeNum) + } + } +} + +func (db NodeDB) GetValid() NodeDB { + valid := make(NodeDB) + for nodeNum, node := range db { + if node.IsValid() { + valid[nodeNum] = node + } + } + return valid +} + +func (db *NodeDB) LoadFile(path string) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + return json.NewDecoder(f).Decode(db) +} + +func (db NodeDB) WriteFile(path string) error { + dir, file := filepath.Split(path) + f, err := os.CreateTemp(dir, file) + if err != nil { + return err + } + err = json.NewEncoder(f).Encode(db) + if err1 := f.Close(); err1 != nil && err == nil { + err = err1 + } + if err == nil { + err = os.Chmod(f.Name(), 0644) + } + if err == nil { + err = os.Rename(f.Name(), path) + } + if err != nil { + os.Remove(f.Name()) + } + return err +} diff --git a/patch/remove-nanopb.patch b/patch/remove-nanopb.patch new file mode 100644 index 0000000..fc581eb --- /dev/null +++ b/patch/remove-nanopb.patch @@ -0,0 +1,26 @@ +--- a/protobufs/meshtastic/deviceonly.proto ++++ b/protobufs/meshtastic/deviceonly.proto +@@ -7,14 +7,12 @@ import "meshtastic/localonly.proto"; + import "meshtastic/mesh.proto"; + import "meshtastic/module_config.proto"; + import "meshtastic/telemetry.proto"; +-import "nanopb.proto"; + + option csharp_namespace = "Meshtastic.Protobufs"; + option go_package = "github.com/meshtastic/go/generated"; + option java_outer_classname = "DeviceOnly"; + option java_package = "com.geeksville.mesh"; + option swift_prefix = ""; +-option (nanopb_fileopt).include = ""; + + + /* +@@ -191,7 +189,7 @@ message DeviceState { + /* + * New lite version of NodeDB to decrease memory footprint + */ +- repeated NodeInfoLite node_db_lite = 14 [(nanopb).callback_datatype = "std::vector"]; ++ repeated NodeInfoLite node_db_lite = 14; + } + + /* diff --git a/scripts/build-meshobserv.sh b/scripts/build-meshobserv.sh new file mode 100755 index 0000000..7b9f9af --- /dev/null +++ b/scripts/build-meshobserv.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +bash "$(dirname "$0")/generate-pb.sh" || exit $? + +docker build \ + --no-cache \ + --pull \ + -f "$(dirname "$0")/../Dockerfile.meshobserv" \ + -t meshobserv \ + "$(dirname "$0")/.." diff --git a/scripts/generate-pb.sh b/scripts/generate-pb.sh new file mode 100755 index 0000000..15c3241 --- /dev/null +++ b/scripts/generate-pb.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +docker run \ + --rm \ + -v "$(cd "$(dirname "$0")"; pwd)/..":/data \ + golang \ + bash -c " + apt-get update && + apt-get install -y protobuf-compiler patch && + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest && + cd /tmp && + git clone --progress --depth 1 https://github.com/meshtastic/protobufs.git && + patch -p1 < /data/patch/remove-nanopb.patch && + rm -rf /data/internal/meshtastic/generated && + protoc -I=protobufs --go_out=/data/internal/meshtastic --go_opt=module=github.com/meshtastic/go protobufs/meshtastic/*.proto + " diff --git a/scripts/run-meshobserv.sh b/scripts/run-meshobserv.sh new file mode 100755 index 0000000..3314d66 --- /dev/null +++ b/scripts/run-meshobserv.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +docker stop meshobserv +docker rm meshobserv + +docker run --name meshobserv \ + --restart unless-stopped \ + -v /data:/data \ + -d meshobserv \ + -f /data/meshmap.net/website/nodes.json diff --git a/website/android-chrome-192x192.png b/website/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..b51ff353a875830ffbdec5ee073614663a543804 GIT binary patch literal 16022 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 literal 0 HcmV?d00001 diff --git a/website/android-chrome-512x512.png b/website/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8e334c4d8bc9d305c9cd8e422c4aac794f94a0 GIT binary patch literal 51355 zcmeFY$2 z)6{_hBg-3JkW6a^i^y1cN&u;|b~!Pm=;(h*f#@L+@Ab9AW(Ll!s-PtYK_=)c?lc?ulk`=9^*ySbnTDFE&qCSCjg?~DIF zNBO^-|M&U-Te|-zHW+OGKdH=Xe*m9XuZl{^zf{2>GeJ_!Zl`jM-`rlkl7qc1HKv=X zUST$bQda6RSschQSz#3|6e4w)T??EtPbK1P!CYPoiCxARwPAqsu?uv-)mv1BKd>hy z>1`8AyAZvXEm~&`IpX=1ME!K07yF4B9C_)&UgY`$sH1bATd9cz6I@0*(x_(h7nf1b+y)R>2~l|I$V147BX!%{NM*M!)Fjr&Dn zY5JQT9?g@90S=+bxt%XSs-UdU|3xWhe7J?MGg`g3kY_MYT>$KWeTlg>k$e%bBA3-3 z=`Vl|6srBLe_;uHi=AQ_Or{eR<#8Gnn)#-lx0}m$sjmDNWj2Nj2*?b`fa&)B2YZ?C z;3tLZ^~`dmyi`S%@2w@O2ⅅ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

?-WEf?DZe!lZw;q277U#`jj`)7_;( znzbP2p%5@8dKL>6@7aYn)Vr$A<+)hsDy~j0tc@ZT-%OHJ$kdT`X+ibC62whOqvw5W z`FPF>3RH97qs*Vw7W1{;DIlz#Ldzm#n|&M7l|v=}k(F8}9+D9R!>%Tr4oxaBCWX)o z0oU9oYHs?eMcgz#OB8H^VcwC~DlBKk>fZx=T5!&T`dK)%EG1(i)pD@hNUX3YLG&3?n5;OD3~ZvjXDgRrH^knoElC)hzE{h`}o7q^y&Eho#8dmP2>fHS$NmCq0bG`&33z7J>*9z#F}}*@>^0 zNyBOqldY&RWWMbo&gOIoqH{6fG%oYiEUJ#V)W@ofJ6l-2^4~JUSe!BeQez)ZhY&7F(9#doZIa3{jJ6^jzZov0C4=4p zw0woLS4Q6d?s+ur#2;`I0qi3NN$4{({(xD|Vz`ph#;U|uvRts{bOV!$H3P&r}&Pr~oJK*njak~IO9^lrx$3(d#Hd_$!R3$D z8H=^PC9D`Q9;QQbmGwr;1c1xEex!VA7p+b*R+%xK({GeFa|(d{)ztafC$rsk4Yj8j zuPs@}j|F!kQzTdvK;(3GkN!BGJ5IG5C@OwgKCg&JyU;hr1$deWeOj$^J(xMy|94OT zZX-a16Sdq|KoN!_>>EyXEQIkudQ@m63}kVku*Xk~AUh2wdv@vXRFoObts)Wk;fO$* zSYKfj?Ec5|t7+yU+5!$4%h@ZAAG<&hfb{-Zgv%NmQFG!Qj}m76RStCq zM|zGM--L<$ps7K#K?(4;gOoT)|027Wl#4_@QlJ|Gd`@=tn7KtM#QT{ov)Lfi=$KC@*~D;2IY zCokBkObFmvi3f-xLHSQFskWGW6`%E7{>I~i@`u*x4-i=EQC*QBE?t#r-B&>atwZL$ zLMyiXwwp(TMM1x!MlhWQq^;xeQ@SF?9C@pL>^Bss{3&*L^|CTh0=Xq{f5)?Ku3iS8 z+vkm(QqrdmvFhcNdJ3Q$%H;|&z~v)|4gRvTFY4rLPo_y2Yrfri+Og{LnJmsgkAF5h@fnuwT0cfky8YmhqdHT)?S>D4REiJ}nA^&p<)}rs210gHj1&Mlg&`mYym3rMBU14)LhWnlq^{|1q+CY9U-7fgog!m`x{F zg?=np&d}ez^hKRMiUA97ganbY+Uk6tWIw80U-=TbzZY9vQoa}r)G=Qc7$AL~B!76P z6M8?LafhKm4Sr8iU=&ahFlm+q&AF2L$|)Do>PYTN-y7s__;{e*nU5PgK)WSl#9ucv zSbManFLM=KoCt3%vVI|Fqo?xiHP&D^#((7zC`c=_uK0UNC;EiboA|)pqm=aebcjCt)I++RKB(PsZdXP} zB)I?VJ-6Ze8?`%Ian>Ge9VV!Q#Omz^gTae3)bjRoAsn2E~y(1a)9L7S4_t`Bx5k3lCP+sp}6Gu{p|9)PA1NEfEHrRueZ zUjjZ+T(})6ab3S!tzAqebzGL~_8D!^Fk2(9x!4punY+jxAp}$dcPCo7ei@tI18-x^ zq|MjL2S3+&bcrMkn+rr~i>fSLzEsn!LW;cP!V;>;>@&GivFF)pbXF7TOeWgmr~Zf2 z@BH7Qab$uEcd+La9BkXL1DgrTYHJ^=kzdRYT;*N;j__gF|_N&e1$w$rjI*}wP>w55kCWa6J=I1@*k8(a`Bg$s zw?4D#q&r>@aqE=qDg+|R*MpiR&rw`-P+@Z-KMer}B0gAO*8U`=?%A-2!KS|b7)ItS z@_psO($V%gd>Da&VFB@FZbU;K`R{mFUXoQ3sX1U9e<8E!-SIv%5^Af5BI?nigS9@D zk%Ncm=Gam0t=s#*t#w(HwSIuKGlGZOp|UIQzY~+9Eliy7$WuGU|x)$#Tpr z5>@iIa3?sQh9)UHI;%ii#4y6|&759d4N5hj9=0F$a*f*hoU_Q2wX^UC7M{limPoDb z9U(IS>+jgb{cEI#-9xOF?{ZF=H5Bj|*-rewDQg-%6d(A#MTQ_5d7zs82>bwGnIE~$ zzVO^sHKNwLW&7@0EE-_i>wsEY%4Q;8A%f-)zPrzuwYiIdA~MCE%h z#3}pBZgc{T%%X@3y=Nc|EpZz)|}87bs)@ws@3Cu8z+Mqu#Y;{DO*vSp9jvWz0RMtWNq-RzZeX*7xocPFK89p zGI@s>j$(-eUTOQsYts)j5u`WJWO1g(3pSG3M%xCK6TxNfsxz6W5veDl7zHnFU>@;D|lbL%I#Hsu@cEG1g5h(sN2kuQu~QZiST4)gaXs*Fvr`x+EZtz z7xfRWF+eX&&$b66>OL1tWL&h9xVxkP3JBoQ>CLEuILNiN8}u~knAs*sXo+?@dn>&j zUSsrBu^V2#&L)0oGyD$m4bUyuZeb_&`XUGZeEzOsOh63LR;@Vjd^1&V% z#MD3?I}%yQv;C(Xr>YYHMu#mps(TT;iO28C&j5?3Bq&0_ zzQk>n0$Czgh1T1>64~?+IAEB7X(UBNRfu-EclcWbnUs-zeHvI+@9yU2FZ@xF0H43D zafYbuEvcJ;D7Psx5uRk1>o&vrUlUmK-IMkYa!)KLoIH%S1fREp* zq;bFUx-(Ebg@xeS@z+rwMz?+0`7SBg1kBV1jkSN9`AH#*ELPi`yOdI_Vb&Ok{+aO1 zYjYvl+x@g-vScUNHeb#(Nq2uhfa{B&-a^hjzfk*qFzdAYvsMSa%hjIl{pD_nvS~WxG;^Rb>=rY{j7V1KOgz=n2E>(%y7$@)|}=J^^Eyy10MNHGmsN> zp~!0@HCzX#46JDHOYiByE8RO-1b{0qnL!3O{rZ^@`W3)|Q(V|@=iE*(j1lN&XH7aQ zrvUKu6Hnzx!GeoGtn=Gz01l{!3@5`80uZM86S*fQ*y-=*q9>7D7z;}F>#z^boF5Nm;wuGo>=+NIv=g~oN9tWJ~`MD zQW_|j$f7+=b8=KHK!&i4I*FlXXIImx`g~L_MB5`!0FtJj0{5;%kgIh94qMNt&N)+T zasn7{0c zZtHt%^+^o$wbn?h&6P1R>K*uy8>mVQJHS)+yC?u>xnJo@KMGLDkckFnJ%Bp~CgGXK z84x!QmZJf*xngs963Mg%&Kg3wW$+iNk!NqB$uc$mulk+D_DUf>- zpa2s-PP^g8p$dUD#uPUj5!{BhOC(@EN0e2RGNxXB{$X%3MMX;)ff)%yCjP^4K6&^0)VIxOe!%X&g^L;B;|C@}MA9RVAb42@zHusX z%S|0+#Ef>eXTUQ1+pP>Js-+qqlZEruq9o|kHBQ`5BB}Dd*Dt<9DDqfB1GwqeBqWMK z{8GDYeVjz^#@$}&h{~g!95@oJnb1Hm-WH8eUrYd3?Q*{WS`ozHxNl0DzXK?~&a0PB z&-;BH=NBPfnWX$*@9J_t#G}9$FWx_)1`NyB3VWsl^ouipPNGJZJ3F&VN9`$~ah4;toO2TDt#lok_^xH2 z#D6_yYXIGxk`2ifA+R0eWYxy4j#>TB1~r5n>4+T=u)8%LvaR2Bt&3!?3#|=X+ra!U{1t(fn{^`K zbtK&9D3#pU0T8^W(jF3LP)gj57y^j;aX|YI)^3yOBb0OWZ)j{TOxS(&z~Tj@})aZ;juv_oKK7y{a0O9;);jVCd*N2m7 z{PA*IiHg#e3p)lJE}xdce8+`z*`7gJeQgIzX~}PG8QU@;=HCi{k3JRBq~`f+ThFM5 zPY1R>XGS*r@1+Wg9_*)Ir$zo-tMOdi-dBOZSXHBXF#;`Pe?0iXbND#Orlc4=ICxRq zI~9u>t^7Ev?H$0Ao3uu!zyDJ_ApuesyOq#A&lgwvE&9#%01i_7kklb>DtaD6c{{koef zV_vuB?zKwQUu1Y3TsfgK1CAD{59}rC{#v&67ne7;F}y_1rY@D`+4g4A56`JbW|>3n z4mU{AS1aD?bvcd#;5(o+DsO8-icR_1K+)uZ^@AXN#y#Mn>+ZWnoBq4pi!60i_P^QS ztma16kf8O`(Kd?{(C-?iBkFR#W@K#|!ARa0@uotR&LCF3DO2a=vR0DBt!LuXB>q`7 z+FC8pSokoP>F0}Q*k4~klh=!1Vw3$W)@0XM1!k7Q3xG_$Z9`or(@S}zuav*y%G~Y+ z|G=#yEf;fFR=di;2z`qYc8#br;5tS2k4h<*xU;76#9UBeM0N3T%=Di@*dMt07A*<` z+B85C2lyMDML)OyS>uHmqGM_m;gMJ3Myhocug#d@CH~Nrm?iApn5xJ|+8ViWk^h~d zb@AIYRu-Sgnm%STahTId;P5ge#eW2QDNii{@EWU#qm%X-C1t`6e_;4(X5y;7ue2@C z2#<^hKK_9ViDLUQMNfBpo2`*;=C{^Mw5uux(JohFu0~J1dZexn`&{@5w8ehsVpEcu zfQ&_K4}Bn{%%|5%Ou2*sduNNdN2ame~SNO zQ&C?hb52yyL`&2{<<8>ynhzsS!odUw{?3#0Wis;9YoE+*2?vbZaFy4&_oU^1!q;za z!iVWd2`aBT^d(f^u9@Iv_4auk{Ry+?yfTAbZj6WDL8Sl*zFuQU2S=4|{2j2}2E$q0)C+zbfqm zcS_NxKV^tfqSJ|AT25FW06l{w?R7nw$M8HHEagC7P>YujnK zkg_>P7ynGsv^Uxzo&S9|*GEZ4gBaI`OyIg?#IT?SBmP+!nbMPRo9N!n^N?;jmSrlh zSHiSOLK2=(HZRt-(h{^yD_UjJ16>Hri{}T$!sBXilyuRlD0!PCaQR%YxK|KHnT36ng-PA%J*yHamZzQz6xubPt`IG zv_MXiYtge5FOah86BBv1{5RDI)!B!D_nU5r*)}H=Uu|bfE>egDz9YzJlCV}0uEzDG zPg`7#zmV@+Rvp?^A zj8LTdTUz?A3L4n~tC7Kd7Rm-sIU z2Pektp)~lUAd2CR1`1Nd#mK5>YsZCHWS>#iD$b|19{%F~;MOB6*yy}ZU`(LaqMc}M<+n8iK zlMTX=o6^}xFSRsgc>RN=!7u&0_wsen#i|+Yu4%Oe#iUN5X2J?x6ZLcDFSfVM;Xw}z*eoET zh2@?L>1cc~_&TP$SaeXDYlpZro!KLa%a@^nAC?9vEw3G-&eeZ%=W57;BG8aET(=Yt zd!uzOEgyvg0g>IswN&YI5o|tKLe)9NvB;=&m7~$TiN!e{LBbEG@XGC%J&jc1Q`MX$ zt-t;;^XXUi{jh}?J51GI2e6@?ne_=nWXAs=4SqPHmTIPzD!l=i zzJPli5P@`l0vC^!9*CPdW?|{}W$3muY@kDI+jl0sYo(#XOW>rh_7(->oI5u8;H+GS zmb^+fZ^+h0Gseq%W0`N#@-em8nSO7e;lrm7-?&$tV#tf;2aF!>I@jm|CQLB`rQjkf z*@po~-P!Xi|HB$~7DYS?$uJ^Zv_vuDwBKx6D% zM*?iFBFeK`JHjWVYgw}SZ>&F7&>&5i|5;py^!OmY7hs#AU|-q%eaYQIsnRG{VGwg9 zTxUKyi=p(96koL4ZX{s7LLg!pwzMB2d;fmF4&1DLw?zpwyPjF&2JI^VuB>rmF$ z$Q|>4;cAP)4?3i{;GmtG7xfr~6H=yMBO(B6%_TA#p#4r9AzIh z!N^N%ML6L>d;cGTTzfE7k0OE>N$b(0G4;Zx)~08l6pt@QkE>T{7^i5Y*ZI{yK-whd za__d;R(Y=r2-5No|BK!o9At$oAh{6WQ_0Jx)Mt=-EC%8djpvgC%vOsOR0{R4j=~80 zOgFM);8mx?3~-fLnHd-<;1WINd}0%gV)G^tpg>K(4kuv-L|$Wan-iEH!(N|baTxtV zj+g#&^oivCt)nLJb(m59_hzUfT{%rG_-|he`DxPB>n14TJN&bCE*pi6jt7aF1HQ2X zm4IjA=i$}X(#LuLt+)sou>~$gS@G(h+G3G{48cf57Zm?K&tcy0Q~lt7^6lPBd(W6M z;VyyG&13JNYCFRXio!_A4dbn?(*Y?{aI2_+xM?MiZtrzhAG4g=f-$2y;?Jy&uNp3PpXP&*1`{J!@K*cud>?I&&fCDcCn0cn zDZ=5j^WVq0m%!aafp8k}0xG9tFEs};H3!g;x>7FY@gZh|;P>BvhEN~YcWwj*&C_8_ zwUJDepVibrV{|bV+lr19a8ljFjo@J?%f9uE4cVN}d}DMg#WpPc!a;yJe|P-~J!U|$ z^y7w)%=inWSE^*3KnKn*G9lJdE|u_-r% zsv5)geiGsv1IF=-8#k95$#(h10i9VDOamTj;jqBE+U!)m-~BD*<8eQb!@-16?grK$ zN?wGlL!{RUt`|tvo=vbP7XhNKf@&aDK{F-(u*WNO)9Qplw*}ucqC8+wC$0Z*b7f)u zk3}6MI;*}TJnem^eDCpWjSVcWT?lsn$~5DAu8@q~k!VGf_rUc2EOst9#*%{CV3FS+ zkY*edD2^#|YRC*wTAPzo_gI!~|A&!bXJq)uu$}xBarBH3;9U)#c!zXN+y+qhf0fd4IuY%Z;Lf z@zj?u$WRjT+B9(e)2S6VyUBqq+0U&xXHnZ$h2Si5A!e*h!ac=zZEU!(_$APn!5#Nm zjLcBMf6QOH?=_)gLo`!!0#S`(7-=mKwPR{WhMyitbhyR;et)txVVUWVV-&D^opTEA zSWAgqK)2dG>``rY`Exe@vw-^%iMBN;OU`#+KZp=;-qzn5tuv4ph@x{nyQWO!;Ou;L zpcb_8e{YAo-0GJB+khWik!u#j}SW)KwzgmDVxuFZM1KbWkAP>eeD1(p( zzfPPjli)P?>wl^V;u8_GG8+|g7mMmK?k*b*zxh!babta;Xw4sjOK#<_D?J)jTozM1_js$_1n_OdleW5se-Tb)=0kPb zn8QEZaGJXQS$rmR;Zl88zFxq6S%fT$X;N&TSVbr;Yk1YX#f6_6xlb4pHfu~Cn#4YT zaJVCpP8FPDDlRJAeDf0Nco5Z-ROhS(>tL!rSVVdrj@p?Jw9dixaj&%eAE(R0i;)0f z!$ZM_EUH2K_Qr+$A^Vg&VC_xN7K&zuFDusy)7M(;9zEcw-qr`SPJ%dj*5I)0_%#oG z7fKMWa6OQ4+Wx^ky?Cx$;Q@ydp=9IoTOrNEbTJMv zJR*U7e{@)Mv;dHIYH$<(1N_F?isO=jF2twQ4ETPPto6cDjB9`Iz4W}Bu$>}J0-%XR zwEkhM+USKZy2P!cj#mv$ke=#n;F(sticR2;CaA9o-`85Gbjv|neZc=%)>_pUzNPlO zD8DhYP-U$%Ii-ncas6JqJ^#sf0LMR5{KrJ=Y!aOxN|@3L{XD$S^6i27HNfl^8)qa+ zSRkzT42xu%k*Cs5V6Yl7x@Ky)k#CSm6?*MIgvH1*f;5& zGNvP{OgxXNhAG1VDS?qyDISd3{@2L7MB0zaE=xACfWLDmO%tO*j%Cihv#c;GplvW3AuXv_D zO%f0c>p~0b0dh*5$F4EFEZRmZEUktj?oL0e*g6uwSc&esIjPNmmYMlu)>B~;!*|o} zk#jrU+$+DJ@cxA~k@7(jlE9ozgw;e(SCG{s0!|-gEBRzuJ4+6r<-{ zvvp5eB@Dm5mZvkL8th3;{+$PR6I)iBI4nHI-)M< z>(Wb*5ZuG|Q2s!&v(km>KA7<`SVefc=>|NwpXqzL=u3FRtdCwD?Fb)g8+x_dRP>A( z`Ib#9@RZUZ=M@xgVTX~Rd?4?)5_n)$fJG2&Bs{|JMTVucrq)o~G*ow#lPjI<-%)oO zA9%aVBeM>?pU8XrDe~qM$Rd~d4I&Vj*==oJ%jk2B;&myoo{JU5dMfuQB|Ny1eyV0c zc=QS()%7VJ`MCPaColS!Zy2I=r7?747Xz6g3Awj_J^Kl6l~ZB$Q2~=eJudnUURbGr z0yUe4P2NoCm8YXoi$~ zw)M!L3C5n_QZD)yZ2d)@dC{z?bCAh2_=qgfQZg!9?KMdx>DR8RoGZ)y?4=~SGu&mh zo@F7k3!>+c+|d`Oh9sbKl{=Q&G+%onw@LNH9}`BO8~j4hr;dj@N9`VVYMI3jqOe!5 zthSe*b(T{wDhBNc02JaF>gh^4AZ-<8QZ>QQtPookU8a)pBKv`vj?rtotRE}#u&>Av zM*$Zezn4@7kiBL+Oq@MEfAWK!^C1P)?WGf^qnh%EAi9#;%_JE7URdf#2IOwgF^gpy ztccF&a#7^gbT`#Lr1Nq&k@lL~=YFn)ACakDtj7*4Fv8#ykfyB%B4ZjE{-WS#*d*m= zPOclEq5B`)svB$1uvGmDgAd%|%>1AVlaq!}QGe8@G7gcx$Ms>4X7YZE3zxLUYhFb} z${&j-s5H4#;TH&O+DV_xmaAEuFe4Q{Ceh#1T;8XZ2q1nca>4Qi?+oA_B*?FyxT+PA zrg!^>u1~uX&0(D006v>n1{t!7A#-QNq{5i9@a{a)>a<e z&Y$P+9)k{a?|B#oqkM`#^-A?tEbh=_e0D&pMuUPbT=WFfC;Z0GLImFqPv;;f3M&X_ zuhJ!USfdqCMBtZvOmXn(In!}jsmP4Qa1*kAF?+$38i+6HdesG~j?VnW&hy!eea(y; z-2=9;-+mFS0d?C}cEXFdmU2$`WfUFzd*s)jxjVSRL1 zZHuuNUuIJovhaecQ5DMeRdJ$cBQapRR3>Udcv=3Yuf<^&Y?b|oQ5 z)leQy3;WqVi`J(aw0AiY5Ft%o>h?KNF~D}Z>JySyGJ?;Ail~)XM-vC0{PU-gWOa-u z$gkz#=8g{@EfFLBbaX58`|)`)Gi^U{-XM&^dJbs1lz-XxO*Iuk1uEy>X;t5D#`Hik zoD!@-b<5*0vI(AnD1%A5X3+Wi!f#T6r+y4yW61T> zQ+-4*dJMl@9W4Q7>`Bp2-nLK-|S7jvZGV9=@i@+B^Hy9iP{k)DZA+6%CiSs|;lkeT^qD(>>CM?2+ zpEISlFZMK=+xIAvm!&Yuh@m|Vsg0V%I?xM80xXGhzh8`XSUglH)%k_MP5t9d_3($r zhygBqf<$C7_)MSKr|R&jlj5g^US_|8;rdIz(hvcV{lB=7^<-116P>wO(x(4Cq|$erdbBPMoM?xyT6+!^@Z@!Bed zjwk>)GVnwQKOF`Inpk+lmyg^5-~=7U(77~|V$;WmcR{a(6Mzra$!b5ypei$#il`LW ztb_KYBxvP$n7G7}BV#O+6~!;iz_Ve`)osd2O1-U6e3?*pN{vA4o7@Yd2T}1Xsi|k2 zM(uP$>b&X*td-2Pi^W}fkSyO5VX}7U5o)IG-CH|m2a_Qm20N-fC9=RLY=hw)eTST@(l5(*Kk`V)+Esi<=eF!%#H%9My7uE0Lg(?;W6VLdX>nWb^e{;1KKE z6Z5n0_e*MT`d0d@G4j)5(Cjig+~|0mU(3}_nlZ&-y77G9TJZMJVxp;Q0L|KZJ1ygJ z%U>`##4p)gCt(B!OPZ)?42@m@&i&uKu2Y@KS{GTfk(qsFz#UzjpdLNHaal43C%s=<`Oi+o40X0oEkQ`0tI@Qi3L8KRpp5a9cb1QA zv`@)UWN2CI_Y5=UkaPWB5kPw<`z8vDW;W+h6 zSS#72Tx*lS=e_wA&W};%qXo{%HSNW>>7*$aBBTqOR>_IfXq8nfN4`Tow(~d#EF#_1)kDtCp(Xf;|T^R`*SXQA4tyj|8}=+sES$sO9q>;(cqF|8Yb@6i6X*VT*LfKX$Li zBxMI0@+XJIt#C5XBDX)KG0wJki*t@dCcNkMfKAQ!!XWE_i{Hdm&A^nm7GB)vm8T<| zJn#Pr#W@jGU=y{3rqttO1C98rX`z*z+eV6En#1V6v55wv=bed!eb~39fPkF06 z=6^V6w7skel@8(ZptUf|a$I1%Gp$kSCTbPsfY#Tr20en5og~y3`#r(cy%f3q+>ydM zlg3;>aZPI9gtyX4^U@g_2J^XJKhtUqrjqf^V+Ho{v)8({Ou`qAM!J3E2nTB#Io7+{ zhA~icV{;FAYmGqZS5R8#nQ&^qn;yr%C8lU+QZPmGjhG^5?C_VQKR*j1tDPp%WX~%3 zao_sk*52nl%voG&XFWaG5w#bVHkhM060*QAm)xHOIG!!wKOPrT=2}^wR%-6N0aJ%st~XKgE|{iRWuXRGu98<39ocIV6g2Teb)QD^%Y7 z2P{lS-25T$aUuagSk2=0>Cs@VA!m^0I7tZhnt8DGPVWY%xg4?1Wi9@RF}L)BJ0+k) zl=+F6HS;^6d=O!sLj|4((&`=`1YgZxi5y*0WxclgRyiA)%IMsFMDJSL^oFpC~9>?-^0^b=UDr!fm<9embpkRtNt}>&~Ey z8n6CNEoO)lM$um+0VVVJYgQ|Bg}ZsfsPg>1{tQ(6;r5sctFsWreF(pOrD2~)ogO4(YQ$N58_3*xvyW5A!p6H(^s|bo~mz?l3bK# z{p|s$45YKZLso0ihMt=(m-;>v=5EpRQTSC!8gD?`mO4vKZs~MbbQ-+*H;$Z~ckSsw z>byDL7Qo!REC)PZ&#gBCv?&ZL$E`g@fJkN?(6YQ_fAH7b1|y8GK8 z_%rXLuZv_(-CO8~X^KaOF;cN1h2?n9u<1GPwK^D9Yt6GRlrLX*VCx1PrM5DYv2mGP z@A9wC$nt^t8;8wo3X5u6?7c)OFxGr@JEXk1gnN-F(#-H`wx`6BITw;h@0s4w@VaJ< zCY?a}-eLcey$j>!==Myx#MRp+Hn*de)rs<$ZIiP=AYPvfx~)A10@C&G*fVGEyJ~(N{dvLPt0n{IjIA6jrNh*}24*F8w!kJg2V}xs?tq8@% zrI1lZYNx@OgcYgPiF(`5(Kk&9^Q_%BuL-c;c6K58V6rS+YRbycFyUX8-l^Ri$sFF^ zzt0S$TEtuYhck0Cb8`>GUXIj_gf#v!{ypJ}I14-!&Fti(Jzg9VInckSI{HOeV|za# zc=qk0XkDZ&3Y;42GZ-=;E>0Yv|TgLzk6zw|HPif5GC$b}|rd5lD z&-k9uHep~q`TSTUzxtG;jneyJ(CowqwR1FhNJOiSn(qx?3~*N4+LbQ*5`lTJ}N#P7`reZFAA>3 zOWHO`j1P8~8TLQrWCBvkXQ30^S<$f3<;;9n{ZZyHYrpANum)gYfQ^|SABcVR;0F3; zlSCuH@v*dgp%<&(?(=8&QuXinabFl(56UzcIcot-%q0TBWhTTNO+Td9!{W*eNtgO> zm|{wYq~#F!*@Wgm7Nz5komQCWF$2mhbu_ghFO3=47kb|jA&slk{LpFxyBCi{`g&e* zFBPypv13V=_MJCa1JcQMM^CLT7Jf73K(;$@mi<+X$)bRIv{gwIq?R{9feB;yVv;tTt}UcWQHN-2^ewYV0XXS0QgJnjDT zvv^7X@S=8NwRfoRPwqRbK3%tvZ$a*`>}sE^)i<}H&M>$dW_%D-Sh~qV+lsoS`v(C; z`H!Xp4{D8%2UVw%y;_i=>naO}UX4fA%bAR4-(NTTzR+KpI+U-wchZ0Y(&MWSc!HzZ zR{~5n%@S+zwp~XJDDL=+z8TP=CBquh<35&YniEjAT#`id?8o2s-!NA!Ht~t^LYT|} zr(@cc24f9*IKI@r=+E?m=05}Qfxg$Jb!<*4H_yWj%WSpMaM=l%~a%9EdR zrv*#gD`_`hSY5~F_7J)eBwY?__q`GmGZuFV~@2;DVWm3iLq>{2t+8Z8j;O<4EN4HDlJr-uusoatT0+ zhv353cnl$h@24l0!m#|jt@Ali6<_@-Ck5E)T&BY^H?++JTttk>gm`ARIK7DH+4lNj zb9{k*d%>AP_ILJoPb|rvv7m|EL7n_!_e&g;ct}_ZG%;gKP0^flJ!aD(FN$7G35L7{ zI0T^Hc~LHffk2#o2caajP$ z6vKaDIb;M+TIDz;(!r&T6lv3+{v6=EnX?SBE^W}ZMs7Y2R$Sj-7^u}=b9{fuj8Si0_6ipnU)w71P9l8`C_)DdvU10pe zwdz)!DQ&og%IU;BSTqkQdajcrY2TRQ{SYDuW7=*}6odXs?|g**@fRn`x7v>xuu!0= zCq|74jfl6eZml1-Xnitkdoh43Q1Zt*Ua9IbE{!^fseMx<5AM8iLuxGZ%}accawIZG z%-SqUf(0lis&q8yn2@I#l1;e zkZ4H57k+{}Lym^!GVT8>>Za2};w*A-QaT1jdiggL($c}Mx8+GKVdzQsWM0$yL&$7Jw~H6=l> z6o!21I`O%$`69qtiL=J{Xz~|1Gcw2n;Rt*(Yz9~fE? zZ4UOM_H~uO_kugq-;(Niv_mLfS*o&4Blg|HYi6DjfD5f<-Wu1d9|%&ge{oyv8?Cq# zrdn@%VxfqeQ16l3Ch&lqX;-<>i}vB~-FiGL16cI>s&yQ^(Z^-0ZbAs+mKXf=%5`N( zi6vN!Wa1Un;ShJpgs{YxWm|Mu6AE=>YF$Uv-PN$Y+JBC3)=pP&ZY=nu>Bc*U%IB0- zdsyZ(Ii@g-X_$O!z_6&2pVLC)7T;&>`C97)ej4-qxgtRK_x&d7XHJ>to~~(1h#sZS zP~=%o$L90goppJst2e9k{iDyD#u=0UzAf~MiNj-}v3}<^qKR8nG595sCmYj}FCp7D zQsJE{z_eS_UtJcYG-AGYJ&+fV8tI@EM!YyGs936_luq!0LxhGg91O+j2*r_NchOl zJ)NW8=b!R0`Za1e`Jqk=7*wI~7kZ^aQP4{Q>w~cLmUnrPPyfZQEP05>_n0)@;?elL zrglU_wya-nb4LJ0cGV%7Rn7&x=!<8B8BiMyUVM@r(koWb^W9(&bj~gVAscX zkp$3hCe^>fb0ZVnsomP_3u12-5)N*vHG71+F&1-@*SXk0sv!SPtTSQgR0%mlIYf!3 z2*7#6H^j;8*aTUcSh=@_li}J!i^gQ%A;l7=7><&~)e zh|^AsxJ2qdTpZ%Xy^q$%y5hMP7zwlVR){R88vhNB=|+T|(B)}S)A5wCOu11m#wL%2 z1U71z0WzFU_;Q?)&t)Ru$+yGl5Fs;fCV?dVV9FIa@v{>X%e?j`vt*$9A5+mn0sT$( z-rKoBEgDRfHS;JN)571B5VvVr^+4=`e=SvgF9YbzR(bmqPZ|$Hg9<;!V{3XmHw|TL z+Dez^K`~wdw||C(2j*@0otQ}3@gUajP-G=SI;Bk%MIAS;yt6|HQQjG@&oYRyiOFOP z)m`{n0WG)v5i++rb?wb}$L$p^eQef$%bUDhabEqNPo z9!BNb_Ux?Ze@A~-O?^LfokGmJTdSSNdncg=DVH%hFG^6?kF`E=;c=ICGRgX0M4Fn5 z!Yqt!Q9Bx@nLB5X|Lzfc-{PZ8@WSVee2Mp>5tzE`HEPifW=F>(T_bduh;7DRanPQ6 z3}eD=8C|3EIZIzTyFq0UW7Jlh^}k{ZNxc96-w2RplhWkv9HI+LkSwe`ENf_=McAV#RE>n`)$=!GQGoBB;d9m*?o0=yt2;2%oUx`A>;q2M5|2L&pxI7 z(4YRc7%|y+(gO#!2J*7+c2e^?Jqk&~9q{)O3~Kf;68W+T)etX@g2k6AjEh!!4tIB{ zs++N5cLmsTk9m^ED7*c$sxPA-RzLBnzUN{iFnAInGYkibBf7m)&oKW|vu@_^C(hfq2QcWr@AGgkY> zq-Ku$SJhp-8Tx?Ca}bx_L7avD4q=O52PO5ZbOA7Cy;v+ z7Ic-2iAuR=hBFsXoGU#-P$;n6X&AkH>kyUN z%4zQ|;fd!*z)Tpd&I`o29gBxp1?5DSh04$1`gw+5Ko{;FHC}0@YvG)k{TE2;VMt@j|$%|J|l$nDjX*(lf2>^$ZTMeQi+M zQJ6fTeVNJqQx5r0=h-ZIqCn2_*k3;m;K|3Nm9rM6U+Iym)Y3j(zWuHRm!_?;v}XnP;+b3^b5YwtR>gs~XA%|Nc2wyI$7R zU7~k*_WJp6aEQ^K27-t~rL^coR!8dEG2ZHmTR@2#?|n|ZxCtKtVei66294>Pm7- zVD|3ct9EQ9JBOPY! zVBcowoBv2y*u`x<%hdi0Lry)~wHQ)r_*gbdHa4HmRB>SM!>spoGc?YZ*24Mvu^_r9 zjx5805IyeyNttgf;L1Btm=Ln{@cXH#b%S{~iyhqC`I`E%6Q(L}sKGPxS|9OI#rVKa z;iWkM*DD#{9TuMLA|nJT5u+VThUChHsohhV4^Gq}yMIrANL?6!1gY1UV=y}hqgRVjtY4{z&E%&a4)6 zqdlKVCkc`PqVDPOo1hEABZ?$s<6j7&XxX}4MCruKdNm3d3D-Acsi)4%RvHQ&sq*I% zMjf;l*%Bw&LbWs79}LYobNsE||4d_o+1q!Tj^IG+yJulSNV!#6XHo*I~PfE(HxtLcqkG~#$1GS~7@28^N9tC5jy-XbO@l^u`2 zQrhAh9T!RZP`EfcmHYbokiWIu`>#5&GpPlpwR;~c2-Ux~I-hw_g2;i73~*)jIblB9 zAlJ4c5cakj@&1<*gZg;Qkazt7k{Q6yfgo5bKF4?pCp;5= zH%2YJIO25YrcYqkrTd(zwEn}_ryiK!Ze}!XogMZuJ5DwG7lcfEZbvXd6&T(SK=bgo zQ*e~t)L^{*B#!pC<-~rfkqDd0j}yngx1+w20vb>@xcSb#-cf}^s#MDax2ySbUP#0G zA&3>5+OPycyS>b=NyGpcv&#bx0k_V;%M52>I`HiYZZ0-YDRpUij6ng;3*F`#LIKy8nQbmnGZ5q0viYi_0%_n%WL6P4 zOgw6G-lRQzzbLVFMP5^a#t+cfe5>*^==~!#f_srweKSc87-eMRPOAKT*o?F)rf9)}p)P^b+g2Lr=;JWC@!$@cf?M6F zU$GXBC9W}!RhM`v4N4aC8a0 zO5y5wrpa+2-g5vZ9~6FnKCt7$K@#BxJhZxgan!qbvVoIeMH8?O^RX&=z~4Je05SZ~ z@_giDlT?7GD)wAnn9vZVc|`5`PV|2;7JN8e2^O7y9(@h+GUsvcM8SVm(scVY;Nora zj_7zYiH2R82abEVhviwmrr0DEa_W!a>jr(szp*VFDL82<7#1lwaMwiau|3D{{z5RO z#T)AVy;S0wKT#0t_wBhHwb>XNz z#6?Y(NQazGXU?E;Mr0^Vg`UT~x!1FHcZE(cSC6Gxeg*9HQeaM15oL=wW;c8p*$YIx zDk@P=AU?0_nfSYwz()+rYF|mRAxz;;sKW3$No-_j&H-Cc|5aD=Aah>d%Uu-T0sK>+ z`_PpXo6MJ}ZD`{IjWf z9NATSfw9JF!Ha(kGbqR77VUJFgpLGKXg8Ea&>2dn1v1r!PgsaWDsG&0tXV}Q0i!JI z1coQv^k>}*oSh5Q%sF?S?_DW{RvBj?G%KhGfPmyyo|tcUCH4k2V4&bfKWcc!3SStbhwGx zY$waiO37(HV@h@B9>Ka~Bqh}tela`-HKPmr@0H;bzCRqy%CRcZ=HjBG==hqVIDm^dnN~W{ z5DGWH@jADk8UD0lMQKROlQ16uZ8$DITI77;QT%o7-|GPG;~wwsPxTFfymDH=b=0EO z!5j&)r}f5iivP>XnMCL=;O(g;{o{S=sL7eRrJ}QOq=5?vNIy;SVV=*-L(RkAiz9dMD_a9+Z$~yKzSV*+pSbit`G{C= zXWL)9oF$1vSF|h~PZ-&w05@mQVHycd4m36JXSJb8{p{N1bkFbkDfR77J{-Zt1n?_HJT05h~`oTDZ~X#Bo~Y znwDi-AXk(3uj-);hqrA0+tJM=M<^3djp)6W+aXQDaEIvi+p@h?j=|~?yNK_iFea>o zNOv4pR{8ZuQNKJb=zZ)jvIzwh1t*jMtUU}=t#}XLqOToGYusmpsxjQSae3f@dE9M3 z_KLL``9QSBV~E8NCy1~joiml&pkk-F7*XtBEt)d0gFcSXOrcDxk{!I-{N5Y1kz2}6SBn4jfP4;T_RbW^I8-H0D1#@xwHBonhTij*p z2KA`u{Rw=zk?IH5Ze}0WWi%f{K<*n_wum?7ew;FOaMX(ie$5I{?NOaDMF6}T zi}6UXpYy^Ei5F^XVHpq1rGe?5cy+<3Pn6EJ*-UJkG_9wx=3K2N)~v}=}1nS>=? z>1v-c%l?QdO245CnjJD@{~XHn8&~bK3d--|KrUz0!NFga=&gJ?=ykbJ$eHZE>HA(f}^%;p_O| zA$N{6D^VY{_6JMQd`UqBz0V!Poe*?gR;Md}&ndsc0A`I!5&Q3Zr2Ak`!qkBJ6t|bNnNEG$qD&tUINSR3?dN8PL)A*Y#m9YN#N76W;K3x-j(vjhekSmYt?RQB@T^JARkXfXKY4GMrH2 z#LpR0vuDrBJjch^2D5ma9gC>R!GwRMTL4Wn8}s?|X1mx(3k`G7%d;?xDryjIO~em* zx5^QI zu?85txn>$cHi*EjTMWKiz!mR^Z12t+AuD6^S^vcp<_3vjUB}azGCS(!4|6Aaa)o6p zMG2OdTk@~=5xNXMCpt%;V=#VNyqrIP9Kx_2_OUyTs49sR!G=wiQA7-QwBU8V(&f6; zGT}$v8UDmuJ$d0Z7nA0mR}2X+Q1f9b7&z;KpLPJ@0f~C96jhqp@SqyFV4PO z{|g*0T#f;Y^}Kotj4$)Df#0`NvO}#8?ECk|uKk!UWooW%DV13v`PAss4e8L7Eb*`m zCtj8AJjt7!y~Gu(*6!y8m1fl@v5IHc{8ME6l3nLiMfXxK%`<63l7wQQt;nO4q<`m7I1~!f#XqSB3eKqa)aMjVOPev`z3?XH1^# zRQ9b(O^!hyhgscdYs!l<%|P;@UQ+-oXMt<*pS$*hyJvLY?XWB#x~e>e{Mw(0FCy`-p*LOmCD}wUwO;?BdHk?~tNB%YV8)t@-D&;qp3|3CCS!2k4 zsMJCuh;YGFt{~4e5eau44KerOx&4y~Tw$MnDoO=>4s^M+f|@nXCGpydsL@{%e&#^u z#qtFn1ueS7q)?`Cq9N@q-{<+Qock_u@K_n+*`K&qg%@W5qcOKGhRMso$+W;_)(Z6! zsGr#mKpzCi*7O!$KSWMP^ZI?FP>F{56jICDE(?w-7a9_8(}mJN1-Ix8Kdjx5*NC7& z25DUu>B)&Tf_HSn*)5PuWY}^_*jY~oK#RmZa34OcZ5*T|TtT@L$&|PW^WLPcR7g{$ zn2Gc{RVjLI%pXapi!BahNHy3fP$)@5Bu;j6zi&6box??zoEos&7I1bo&X9qDTZdl@ECj+q{F!apSP?Qc+AbkIUcME}^f0uV7mC-OvO z!>WdNfO_A_xB+M=9Y`dB)elD0Gb&1Z(K$Uu1uiNgS)$x#BVMbhBrDc&%0;g7__cC} zu6cxuVoW~aK;g4%mHj7TQNjc;;GSb93!OeJP^)c5ibsB`N)4yd!t_#ZP@e+61km{0 z4L8oitz_Q2C-b7!keoNZ_AMFwpsqa1?pE>&hI2`4;d+biyxp9aJW>d}*rXmo)_TQS zthkvLKQ;8r?K|mJbi%7xFY?AUTF0%xQqV5Z(nsg=fxru zu(5fK9_Uh$tE9ESLBSae9&d@jQi#o3FsCNHn{+V>5uB-T*FExt=GUQ-f*YruAfGwN z9)5GR%%`%}IrM2F5x+2R*I56Dmj}&5Xg*_rxdg`XFHe%+#$MN^Gw;JgkmlmFLEEOC zeVu!iGjY9>wuY$r4x}QXakLms0{7 z12=Ah-gcX zIc^^es=}qN8mKI5R_&Gn#NKNO((sHXvEx;+=+eEIux!FfxTpr@oX*zGdJGcZ?Q7P< z>3{d)%-VxJB*+YiE1?n#9294YhoK2Pfh3uG|12>2#YO937PO=ZNcT|Q^!NAm0dmT) z4@KqF$Asesc)K814ZWH;`dO;X3!$o+6uh8$M*F;BAA(bb7Z)z=x2l~8UU*n1w04R) z_>f`HWxH}OsjL44nkE} zxTF6jA{RLI!-;y*pNW**YK1yaK1{{mEGCA&Ps+qpFeW~B4N-cIEzcB@558Ae)-ye| z1j?xGLs>n<=M(>AO88uLnDp@h74DV?8PmR`9>%TkIlYqayLTf{ZM)dHoTJ53ln*3x zZ)|1C#X^XU?O}P_zJ*Y#gQ`=2znaMR&#CGAWz=1Y!X4Je8C@1 z9u$MOKP=7&?0O~xJA0vB3w!9_}cHnl4~Fk z_WAI}v;>kE2q950#R-)noP_7_wfmLTlU%}Eh;mK07)8+bg1+V<5g+6iVQp+;eMJ+) zpg;2^YiXx7`{g(r?u*|u9B|><)89c3lk3{L5DWZl1T`Mmu2EM%sGrB#1a^JnE{cqc!y_yrVN?xJaJ76;Eiq^!p+H@cAOhMb*jM zr}!jPuN#9c%yJA2sPVoiS+XEG;$$ip4K=97xGc&*t{a z)8)M)l9v-S99KmS)U2N%lclI;yJ%LdexuaUz2I!=$Z&%oF!!i3ZM@NA8 z8(8_}m&h2_T~5&a5+V3o;fbFG-C(!4N4-)c>pY$PBDd? zw0tSa(sg*{B~FnCllA#o=VI0aY-`E_KMdG9_15bBv**x!XWSJlYU`6y=43?S>@^ki zUCxV~E3*MS_*7~qb+2lF>TqPT@0}*-&V0;a1|*mSz5rr(Ny*jHnEl*>DlbshnXk1a z3)hjB)#J7|zA;H8y36AooFC}I<*0hnyJ&q~6Z))!oqi=Oo>hKi1_DE;-qDFclY|2n zOZXrQ{*J~Mc={{IYWi1?!AUr z9pq=fMppc_I4#bpgu^Eihw{eX11gFk844Oee_upV$epLfK}_fJy&~Ujq50hDAI`|- zePOjZZ7UdPdx0^#OzEJdIt5MSsZi&xcL=j2?r$Bnp1RY+q%=BB3s71P&ax`02cRjt zS<3KmWfcDWOTCKG@ry?2h^g3b@|1t%z%e)1R+#Wr;suesZaXLHdeZP0D3TA&@ri%5 zN?=cxz|8ELM_nBUFW-#xT8@zd?mNy^kWwBQce3B+*A(LtG`ENPfAYNS6mH08Z#rpP zNPdY~1d)xJ;4k?w^(*D~Elf@c2}T*sQ3q`(SDF{>Ns&|2gJH22a;tkOU!2P<)R&Gh zFi(BZG1+w0^CENJ#0?Sq7vau~Cz zf4f?a{=I!^t*$GmX>RT22xENLa_Z#YNOq%k@O^`0cFEj&x;YQ+p$iP(+D<{?hq?a} z=BkZ)L5baEIfCiomWM6(diX%)r=Cuu%y&nUFHDt-Pf16ukySj{;L&?1DNXAu39f4!4z{RnWOj~#sa>V%4$mNoWNL_8R2Z{otZtj$xPrT zGw-#LUmTbE5`*kq1Sx6|+FUesrM|n~@v8L7RJpq_`Q-DOry=Pg{+L?09kFl{-LognJ7z)Sd@tZ@Ot@`}GdrH0- z*Gx1Lf>1cLdny;q%EIP-Z4TjtD#Yy_q-V!A_96#FdbF<}{mstgX~*~4IFk3d9ckG6 z#0rQXZ!*_xbc>%=r|l6rOdW0*j=n?=wnLVZq_<3#^LR0aIGG$fAl1vHkM<1BiD#;j zlWZ%>Ty_Wx;1wHqvu)I8h(yaE2VLhF^UuHJ9^QJJOfumDSI}q8ZTWBRtC
h%}+Gu&c_2mlkj`raJ@Pd znj%#bKnXh4atZ*Ko{FKrQ|7FUO@><6`N1>nB>gIbD#f~Ue6A$V1CO`vu8QrK{DO+U zo5`#Hr0c1`wu0(Assb;kfoz7n)JQ>gQbwD748&h(|3YHX+3eMJ(!@$bOT7<`M;?w% z!|2-k^@OYBmCa?S;eZ5V(*RWj@D4ZIAq{|8V^sW;s66jmRFcE@49mG<;sdTj!_rr* zf8x2x%y0|5_j5TRog9#k*nGY;eI_gt@#m77siKW+{h#y)7?SMJP54t&fL~XD)Wz$Q zh6#qiL~708S~CcHnxl#<9;Y7LZw+4wABY8h0>�r|0E&&~FzXG@q0_31-8VlOE;B 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 F{{uxJ@8AFc literal 0 HcmV?d00001 diff --git a/website/apple-touch-icon.png b/website/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c96e15a70825b2578ffbb30ca4a240bdfad8765a GIT binary patch literal 14470 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%0A~75I(xE&E3b@-Sml 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 + + + + +MeshMap - Meshtastic Node Map + + + + + + + + +

+
+ + + + + diff --git a/website/site.webmanifest b/website/site.webmanifest new file mode 100644 index 0000000..356c72c --- /dev/null +++ b/website/site.webmanifest @@ -0,0 +1,17 @@ +{ + "display" : "standalone", + "icons" : [ + { + "sizes" : "192x192", + "src" : "/android-chrome-192x192.png", + "type" : "image/png" + }, + { + "sizes" : "512x512", + "src" : "/android-chrome-512x512.png", + "type" : "image/png" + } + ], + "name" : "MeshMap.net", + "short_name" : "MeshMap" +}