diff --git a/.cargo/config.toml b/.cargo/config.toml index cbb49d3..290cf13 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,3 +4,7 @@ linker = "arm-linux-gnueabihf-gcc" [env] GETTEXT_SYSTEM = "1" + +[build] +rustflags = ["--cfg", "tokio_unstable"] + diff --git a/Cargo.lock b/Cargo.lock index 7bef3b3..3d7be97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -110,6 +119,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base-x" version = "0.2.11" @@ -834,6 +858,12 @@ dependencies = [ "polyval", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "glob" version = "0.3.0" @@ -962,7 +992,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1078,9 +1108,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libloading" @@ -1232,12 +1262,11 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -1402,6 +1431,15 @@ dependencies = [ "objc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.13.0" @@ -1416,7 +1454,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "opensesame" -version = "0.8.3" +version = "0.8.4" dependencies = [ "async-ssh2-tokio", "bme280", @@ -1574,9 +1612,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1925,6 +1963,12 @@ dependencies = [ "yasna", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2217,6 +2261,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spidev" version = "0.5.1" @@ -2496,33 +2550,32 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.5", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.85", + "syn 2.0.29", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 309d858..988ee1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "opensesame" -version = "0.8.3" +version = "0.8.4" authors = ["Opensesame Contributors"] edition = "2021" readme = "README.md" @@ -36,7 +36,6 @@ assets = [ ["doc/*", "usr/share/doc/opensesame/", "644"], ] - [dependencies] gpio-cdev = { version = "0.5.1", features = ["async-tokio"]} @@ -44,7 +43,7 @@ i2cdev = "0.5.1" elektra = { version = "0.11.0", features = ["pkg-config"] } -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version = "0.11", features = ["json", "blocking"] } bme280 = "0.3.0" mlx9061x = "0.2.1" @@ -59,7 +58,7 @@ chrono = "0.4.19" signal-hook = "0.3.14" signal-hook-tokio = "0.3.1" -tokio = { version = "1.15.0", features = ["full"] } +tokio = { version = "1.35.0", features = ["full"] } tokio-util = "0.6.9" async-ssh2-tokio = "0.7.1" futures = "0.3.19" diff --git a/README.md b/README.md index 5bd1a8b..86b7a45 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,12 @@ For what is missing, see [TODO.md](TODO.md). ## Disclaimer -Currently there is no full beginner-friendly documentation on how to setup the system. +Currently there is no complete beginner-friendly documentation on how to setup the system. If you are interested, please open an issue. [Installation](/doc/Install.md) is not difficult, though. As hardware hacking is involved I cannot guarantee that anything will work for you. -It works for me, though. +It works for me and worked for someone else, too. ## Quality Goals @@ -46,7 +46,7 @@ On each Olimex, the same software runs but differently configured using Elektra. E.g. to set a new PIN 1234 for "us": -kdb set user:/sw/libelektra/opensesame/#0/current/validator/us "[14, 15, 13, 15, 11, 15, 7, 15]" +`kdb set user:/sw/libelektra/opensesame/#0/current/validator/us "[14, 15, 13, 15, 11, 15, 7, 15]"` Deployment is done via Ansible, see [an example](/ansible/playbook.yaml). diff --git a/ansible/dev-setup/opensesame_dev_setup_inv.yaml b/ansible/dev-setup/opensesame_dev_setup_inv.yaml deleted file mode 100644 index fa1f85b..0000000 --- a/ansible/dev-setup/opensesame_dev_setup_inv.yaml +++ /dev/null @@ -1,8 +0,0 @@ -all: - hosts: - 192.168.0.10 - -olimex_dev_boards: - hosts: - 192.168.0.10: - ansible_user: olimex \ No newline at end of file diff --git a/ansible/dev-setup/opensesame_dev_setup_pb.yaml b/ansible/dev-setup/opensesame_dev_setup_pb.yaml deleted file mode 100644 index cbf2276..0000000 --- a/ansible/dev-setup/opensesame_dev_setup_pb.yaml +++ /dev/null @@ -1,82 +0,0 @@ ---- -- name: Install required packages - hosts: olimex_dev_boards - gather_facts: yes - become: true - - tasks: - - name: Install build-essential - apt: - name: build-essential - state: present - - - name: Install curl - apt: - name: curl - state: present - - - name: Install rustup - become_user: olimex - shell: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.67.1 - args: - executable: /bin/bash - creates: /home/olimex/.cargo/bin/rustup - - - name: Add Rust to PATH - become_user: olimex - lineinfile: - dest: /home/olimex/.bashrc - line: 'export PATH="$HOME/.cargo/bin:$PATH"' - state: present - - - name: Install pkg-config - apt: - name: pkg-config - state: present - - - name: Install libmodbus-dev - apt: - name: libmodbus-dev - state: present - - - name: Install autoconf - apt: - name: autoconf - state: present - - - name: Install libtool - apt: - name: libtool - state: present - - - name: Install clang - apt: - name: clang - state: present - - - name: Install git - apt: - name: git - state: present - - - name: Create GitHub folder - become_user: olimex - file: - path: /home/olimex/github - state: directory - mode: '0755' - - - name: Check for existing repository - become_user: olimex - stat: - path: /home/olimex/github/opensesame - register: opensesame_folder_stat - - - name: Clone the repository - become_user: olimex - git: - repo: https://github.com/ElektraInitiative/opensesame.git - dest: /home/olimex/github/opensesame - clone: yes - when: not opensesame_folder_stat.stat.exists - diff --git a/ansible/dev-setup/run.sh b/ansible/dev-setup/run.sh deleted file mode 100755 index 8eee2ce..0000000 --- a/ansible/dev-setup/run.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -myplace=`dirname $0` - -ssh-add -ansible-playbook -i $myplace/opensesame_dev_setup_inv.yaml $myplace/opensesame_dev_setup_pb.yaml --ask-become-pass diff --git a/debian/service b/debian/service index c00ae0d..10849d1 100644 --- a/debian/service +++ b/debian/service @@ -2,6 +2,7 @@ Description=opensesame Wants=network-online.target nss-lookup.target After=network.target network-online.target nss-lookup.target +StartLimitIntervalSec=0 [Service] Type=simple @@ -12,11 +13,13 @@ NoNewPrivileges=true ProtectSystem=full Nice=-20 -CPUSchedulingPriority=80 +CPUSchedulingPriority=20 +CPUSchedulingPolicy=fifo IOSchedulingClass=realtime IOSchedulingPriority=0 Restart=always +RestartSec=1s ExecStart=/usr/bin/opensesame [Install] diff --git a/doc/Audio.md b/doc/Audio.md index 6bc8fa2..9b04333 100644 --- a/doc/Audio.md +++ b/doc/Audio.md @@ -1,3 +1,4 @@ # Audio Module + This module handles audio output for playing fire alarms and bell sounds. -This module can receive commands from the Module Buttons (Bell), Environment (FireAlarm), Nextcloud (FireAlarm, Bell), and Signals (FireAlarm, Bell). \ No newline at end of file +This module can receive commands from the Module Buttons (Bell), Environment (FireAlarm), Nextcloud (FireAlarm, Bell), and Signals (FireAlarm, Bell). diff --git a/doc/Benchmark.md b/doc/Benchmark.md deleted file mode 100644 index 7cbb481..0000000 --- a/doc/Benchmark.md +++ /dev/null @@ -1,8 +0,0 @@ -# Benchmark - -## with opensesame v0.6.2 - -sensors: ~100ms per iteration (854ms first iteration) -haustuer: ~46ms per iteration (695ms first iteration) -garage: ~46ms per iteration (710ms first iteration, 2620ms while compiling) - diff --git a/doc/DevelopmentSetup.md b/doc/DevelopmentSetup.md index a048e5b..293260a 100644 --- a/doc/DevelopmentSetup.md +++ b/doc/DevelopmentSetup.md @@ -1,4 +1,5 @@ # Development Setup + For the development setup you have to do some steps manually, so this page should help you to get your development environment ready to use. ## 1. Clone the project on you local development environment diff --git a/doc/Environment.md b/doc/Environment.md index 0948889..1fffbc6 100644 --- a/doc/Environment.md +++ b/doc/Environment.md @@ -1,4 +1,5 @@ # Environment sensors + ## MOD-BME280 ### Setup and Wiring @@ -18,4 +19,4 @@ These pins are the same as those used for UEXT1 on the [LIME2-SHIELD](https://ww The following command reads the status register as a test: ```bash /sbin/i2cget 2 0x77 0xF3 -``` \ No newline at end of file +``` diff --git "a/doc/Haust\303\274re.md" "b/doc/Haust\303\274re.md" index b61c880..f8e1021 100644 --- "a/doc/Haust\303\274re.md" +++ "b/doc/Haust\303\274re.md" @@ -55,13 +55,18 @@ Insgesamt: 6 Mod_IO2: 6xOUT für LEDs (Mod_IO: 4xOUT mit Relay) +## Input -# sonstiger Input +- IN1 Lichttaster bei Glocke außen (orange/weiß) Taster Lampe ganz aussen (Taster unten) +- IN3 Taster Glocke ganz außen (obiger Taster) +- IN4 Taster Eingang Innen + +# Further Input -- Lichttaster bei Glocke außen (orange/weiß) orange -> GND, weiß -> Board21 GPIO0 (Taster Lampe ganz aussen) - Button Taster aussen Lampe grün/violett -> Board21 GPIO0 (Doppelbelegung!) -- Taster Eingang Innen -> Board21 GPIO2 (schwarz) -- Taster Glocke außen -> Board21 GPIO3 (braun, needs 47kΩ pull-up) +- (OLD, UNUSED) Lichttaster bei Glocke außen (orange/weiß) orange -> GND, weiß -> Board21 GPIO0 (Taster Lampe ganz aussen) +- (OLD, UNUSED) Taster Eingang Innen -> Board21 GPIO2 (schwarz) +- (OLD, UNUSED) Taster Glocke außen -> Board21 GPIO3 (braun, needs 47kΩ pull-up) Insgesamt: 9 (-1) @@ -92,3 +97,25 @@ Sensor Mod-ENV 24V 0.5A for relais 6V~ 0.3A for bell 5V 2.0A for A20 + +## Kabeln + +von links nach rechts: + +1. nicht verwendet: blau, Nullleiter Vorraum, 1-polig +2. nicht verwendet: grau, 14 polig, no shield, vermutlich von Haustüre kommend (geht quer über alle Sicherung) +3. Taster Eingang Innen: schwarz, 2 polig, no shield, Board21 GPIO2 (Schlauch 1) +4. Türöffner: weiss, 2 polig, no shield, 12 V (geht quer über alle Sicherung) +5. Medi-Sensor: weiss, 4? polig, paar-weise geshielded, Verwendung: i2C Environment (Schlauch nach oben, mit Stromdrähten) +6. Hauptkabel zu Türöffner, geshielded (geht quer über alle Sicherung) +7. Taster bei Glocke, orange/weiss, Board 21 GPIO 0 (Schlauch mit fetten anderen Kabel, evtl wärmepumpe) +8. Netzwerkkabel 1 +9. Netzwerkkabel 2 +10. Glocke Taster aussen, weiss klein dünn, Board 21 GPIO 3 (Schlauch 1) +11. Anschlusskabel grüne Relais (HIMA), 4 polig, with shield; + grün: linke Relay 4 + gelb: rechts Relay 4, + weiß: rechts Relay 2 (GND) + braun: linken Relay 2 (GND) + shield: GND +12. Glocke: 2 polig, no shield 16V (eigener Schlauch) diff --git a/doc/I2C.md b/doc/I2C.md index 9f8e2f7..552afab 100644 --- a/doc/I2C.md +++ b/doc/I2C.md @@ -2,6 +2,7 @@ - larger AC loads (>>10W) might need snubber circuits - larger DC loads might need a diode +- inputs that are somehow near AC/DC loads must be via opto-coppling (Mod_IO) - avoid switching relays faster than every ~200ms - avoid snubber parallel to switch on small loads - avoid multiple appliances that might create interferences on the same board (i.e. connect larger loads to different mod-io2) diff --git a/doc/Install.md b/doc/Install.md index 5db765a..d698905 100644 --- a/doc/Install.md +++ b/doc/Install.md @@ -1,6 +1,8 @@ # boot with A20-Debian-Server-SD + # latest A20-Debian-Server-SD images at: -# http://images.olimex.com/release/a20/ + +http://images.olimex.com/release/a20/ ``` bash $ wget https://images.olimex.com/release/a20/A20-OLinuXino-bullseye-minimal-20230515-130040.img.7z diff --git a/doc/ModIR.md b/doc/ModIR.md index bd7051f..02eac8e 100644 --- a/doc/ModIR.md +++ b/doc/ModIR.md @@ -1,4 +1,5 @@ # [MOD-IR-TEMP](https://www.olimex.com/Products/Modules/Sensors/MOD-IR-TEMP/open-source-hardware) Module Description + ## Setup and Wiring The setup and wiring instructions are the same as those provided in the [Environment-documentation](./Environment.md) @@ -11,4 +12,4 @@ You can configure the sensor by running the following commands: kdb set user:/sw/libelektra/opensesame/#0/current/ir/enable 1 kdb set user:/sw/libelektra/opensesame/#0/current/ir/device "/dev/ic2-2" kdb set user:/sw/libelektra/opensesame/#0/current/ir/data/interval 60 -``` \ No newline at end of file +``` diff --git a/doc/Modules_Overview.md b/doc/Modules_Overview.md index 0d491c8..98c4528 100644 --- a/doc/Modules_Overview.md +++ b/doc/Modules_Overview.md @@ -1,2 +1,3 @@ # Modules Overview -![information_flow](images/information_flow_v0.8.0.png) \ No newline at end of file + +![information_flow](images/information_flow_v0.8.0.png) diff --git a/doc/Nextcloud.md b/doc/Nextcloud.md index 812b0a4..88896a1 100644 --- a/doc/Nextcloud.md +++ b/doc/Nextcloud.md @@ -1,3 +1,4 @@ # Nextcloud Module + Implements two loops: one for sending (`message_sender_loop`) messages and status to Nextcloud, and the other for receiving (`command_loop`) messages/commands from Nextcloud. -Commands can be sent via Nextcloud chat by typing "\opensesame" to open the door, or other commands like "\ring_bell", "\fire_alarm", "\status", and "\switchlights true true". \ No newline at end of file +Commands can be sent via Nextcloud chat by typing "\opensesame" to open the door, or other commands like "\ring_bell", "\fire_alarm", "\status", and "\switchlights true true". diff --git a/doc/Opensensemap.md b/doc/Opensensemap.md index 10dda4b..b800630 100644 --- a/doc/Opensensemap.md +++ b/doc/Opensensemap.md @@ -1,4 +1,5 @@ # Opensensemap + After you've setup your account and logged in on [opensensemap.org](https://www.opensensemap.org/), you need to do following steps: ## 1. Adding a senseBox @@ -71,4 +72,4 @@ sensors."#0".loc = "Weatherstation" Set an environment name using the command: ```bash kdb set user:/sw/libelektra/opensesame/#0/current/environment/name "" -``` \ No newline at end of file +``` diff --git a/doc/Ping.md b/doc/Ping.md index a545f06..d66deb5 100644 --- a/doc/Ping.md +++ b/doc/Ping.md @@ -1,4 +1,5 @@ # Ping Module + This module sends a ping message to Nextcloud if it receives the `SendPing` event. Other functions update `Env`, `EnvStatus`, `EnvError`, and `BatCapacity`, but these commands aren't used yet. -They need to be triggered at the right spot in Environment and Battery to keep the ping up to date. \ No newline at end of file +They need to be triggered at the right spot in Environment and Battery to keep the ping up to date. diff --git a/doc/SUMMARY.md b/doc/SUMMARY.md index fd62bff..8a40bba 100644 --- a/doc/SUMMARY.md +++ b/doc/SUMMARY.md @@ -10,7 +10,6 @@ - [Translations](Translations.md) - [Benchmark](Benchmark.md) - [Debugging](Debugging.md) -- [Troubles libmodbus](libmodbus-troubles.md) - [Troubleshooting](Troubleshooting.md) # Using @@ -18,25 +17,30 @@ - [Ansible](Ansible.md) - [Installation](Install.md) -# Modules -- [Modules Overview](Modules_Overview.md) -- [GPIO](GPIO.md) +# HW-Hacking + - [I2C](I2C.md) +- [Troubles libmodbus](libmodbus-troubles.md) - [MQ135](MQ135.md) +- [GPIO](GPIO.md) +- [Energy](Energy.md) + +# Modules + +- [Modules Overview](Modules_Overview.md) +- [Nextcloud](Nextcloud.md) +- [Ping](Ping.md) +- [Signals](Signals.md) +- [Error-Types](Types.md) - [Garage](Garage.md) - [Environment](Environment.md) - [Front door](Haustüre.md) - [Weather station](thiesclima-weatherstation.md) - [Opensensemap](Opensensemap.md) - [Inverter](goodwe-inverter.md) -- [Energy](Energy.md) - [Audio](Audio.md) - [Battery](Battery.md) - [IR-Temp](ModIR.md) -- [Nextcloud](Nextcloud.md) -- [Ping](Ping.md) -- [Signals](Signals.md) -- [Error-Types](Types.md) - [Watchdog](Watchdog.md) diff --git a/doc/Signals.md b/doc/Signals.md index 358bdf9..26dd412 100644 --- a/doc/Signals.md +++ b/doc/Signals.md @@ -1,2 +1,3 @@ # Signal Module -This module listens to system signals and executes the same events as in the old version. \ No newline at end of file + +This module listens to system signals and executes the same events as in the old version. diff --git a/doc/Testreport_Async_Refactor.md b/doc/Testreport_Async_Refactor.md index ac1d6c5..0d76d10 100644 --- a/doc/Testreport_Async_Refactor.md +++ b/doc/Testreport_Async_Refactor.md @@ -1,4 +1,5 @@ # Testing Async + This is the testreport of the async refactor of opensesame. (`-`) means that the test failed, (`~`) something went wrong but it works and (`+`) means that everything went as expected. Here is a list of the modules which need to be tested. 1. **Signals** @@ -476,4 +477,4 @@ done | ---------------- | ---------- | | Reboot | `+` | - \ No newline at end of file + diff --git a/doc/Translations.md b/doc/Translations.md index 112f25f..a592ce3 100644 --- a/doc/Translations.md +++ b/doc/Translations.md @@ -1,4 +1,5 @@ # Get Translation Strings + ```bash cargo install xtr ~/.cargo/bin/xtr src/lib.rs -o lang/opensesame.pot @@ -15,4 +16,4 @@ msgmerge -U lang/de_AT.po lang/opensesame.pot ```bash msgfmt lang/de_AT.po mv messages.mo files/opensesame.mo -``` \ No newline at end of file +``` diff --git a/doc/Types.md b/doc/Types.md index 3a619ef..84cd8f7 100644 --- a/doc/Types.md +++ b/doc/Types.md @@ -1,2 +1,3 @@ # Types Module -This module contains the error types defined so far. \ No newline at end of file + +This module contains the error types defined so far. diff --git a/doc/libmodbus-troubles.md b/doc/libmodbus-troubles.md index 58b5efb..956dc79 100644 --- a/doc/libmodbus-troubles.md +++ b/doc/libmodbus-troubles.md @@ -1,4 +1,5 @@ # libmodbus Trouble Protocol + The documentation of the libmodbus library can be found on [this website](https://libmodbus.org/reference/). This brief troubleshooting protocol contains essential information required for communicating with a modbus device. It also includes details about the challenges we encountered during our programming journey. @@ -40,4 +41,4 @@ To solve this issue we had to use the right address, which is configured on the ### Solve To solve this issue we had to use the right function (`0x01`, `0x02`, ...) for the chosen register. -The function for any register is in the documentation of the [weatherstation](https://www.vetterag.ch/images/pdf/thies/BA/4.920x.x0.xxx_ClimaSensor_US_d.pdf) or [inverter](https://loxwiki.atlassian.net/wiki/spaces/LOX/pages/1605274474/Goodwe+GW10+ET+MODBUS+TCP+IP?preview=/1605274474/1605274552/Goodwe_Modbus_Protocol_Hybrid_ET_EH_BH_BT__ARM205%20HV__V1.7%20_%20Read%20Only_20200226%20(1).pdf). \ No newline at end of file +The function for any register is in the documentation of the [weatherstation](https://www.vetterag.ch/images/pdf/thies/BA/4.920x.x0.xxx_ClimaSensor_US_d.pdf) or [inverter](https://loxwiki.atlassian.net/wiki/spaces/LOX/pages/1605274474/Goodwe+GW10+ET+MODBUS+TCP+IP?preview=/1605274474/1605274552/Goodwe_Modbus_Protocol_Hybrid_ET_EH_BH_BT__ARM205%20HV__V1.7%20_%20Read%20Only_20200226%20(1).pdf). diff --git a/files/opensesame.mo b/files/opensesame.mo index aed1082..80ba853 100644 Binary files a/files/opensesame.mo and b/files/opensesame.mo differ diff --git a/lang/de_AT.po b/lang/de_AT.po index ede0186..d4f286a 100644 --- a/lang/de_AT.po +++ b/lang/de_AT.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: opensesame 0.7.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-09 10:40+0000\n" +"POT-Creation-Date: 2023-12-24 06:54+0000\n" "PO-Revision-Date: 2022-10-02 13:40+0200\n" "Last-Translator: Markus Raab \n" "Language-Team: German \n" @@ -31,64 +31,56 @@ msgstr "🚨 Audio Feueralarm!" msgid "Couldn't send SIGUSR2 to other opensesame instance: {}" msgstr "SIGUSR2 konnte nicht zu anderen Opensesame-Instanz gesendet werden: {}" -#: src/bat.rs:60 +#: src/bat.rs:66 msgid "🪫 ON {}%" msgstr "🪫 An {}%" -#: src/bat.rs:67 +#: src/bat.rs:73 msgid "🪫 Battery Capacity is below {}% at {}%" msgstr "🪫 Batterieladung ist unter {}% auf {}%" -#: src/bat.rs:83 +#: src/bat.rs:89 msgid "🔋 ON {}%" msgstr "🔋 An {}%" -#: src/bat.rs:89 +#: src/bat.rs:95 msgid "🔋 Battery Capacity is back to {}%" msgstr "🔋 Batterieladung ist zurück bei {}%" -#: src/buttons.rs:496 -msgid "👋 Turned PWR_SWITCH off" -msgstr "👋 PWR_SWITCH ausgeschalten" - -#: src/buttons.rs:505 -msgid "👋 Turned PWR_SWITCH on" -msgstr "👋 PWR_SWITCH eingeschalten" - -#: src/buttons.rs:539 -msgid "{}" -msgstr "{}" +#: src/buttons.rs:519 +msgid "{} {}" +msgstr "" -#: src/buttons.rs:559 +#: src/buttons.rs:542 msgid "🔔 Pressed button bell." msgstr "🔔 Knopf für Glocke gedrückt." -#: src/buttons.rs:568 +#: src/buttons.rs:551 msgid "🔕 Did not ring bell (button was pressed) because the time 🌜 is {}, {}" msgstr "🔕 Läute Glocke nicht (Knopf gedrückt) weil Uhrzeit 🌜 ist {}, {}" -#: src/buttons.rs:580 +#: src/buttons.rs:563 msgid "💡 Pressed switch inside. {}." msgstr "💡 Taster innen gedrückt. {}." -#: src/buttons.rs:591 -msgid "💡 Pressed switch outside or light button. {}." -msgstr "💡 Taster oder Knopf außen gedrückt. {}." +#: src/buttons.rs:574 +msgid "💡 Pressed light button. {}." +msgstr "💡 Licht-Knopf außen gedrückt. {}." -#: src/buttons.rs:604 +#: src/buttons.rs:587 msgid "🔔 Pressed switch bell." msgstr "🔔 Taster Glocke gedrückt." -#: src/buttons.rs:613 +#: src/buttons.rs:596 msgid "🔕 Did not ring bell (taster outside) because the time 🌜 is {}, {}" msgstr "" "🔕 Läute Glocke nicht (Taster außen gedrückt) weil Uhrzeit 🌜 ist {}, {}" -#: src/buttons.rs:627 +#: src/buttons.rs:610 msgid "🕶️ Light was turned off." msgstr "🕶️ Licht wurde abgeschalten." -#: src/buttons.rs:637 +#: src/buttons.rs:620 msgid "" "⚠️ Error reading buttons of board {}. Load average: {} {} {}, Memory usage: " "{}, Swap: {}, CPU temp: {}" @@ -96,25 +88,25 @@ msgstr "" "⚠️ Fehler beim Lesen der Knöpfe am Board {}. Load average: {} {} {}, Memory " "usage: {}, Swap: {}, CPU temp: {}" -#: src/buttons.rs:650 +#: src/buttons.rs:632 msgid "🤗 Opened for {}" msgstr "🤗 Für {} geöffnet" -#: src/buttons.rs:666 +#: src/buttons.rs:648 msgid "💡 Switch lights in and out. {}" msgstr "💡 Schalte Licht innen und außen ein. {}" -#: src/buttons.rs:676 +#: src/buttons.rs:658 msgid "🕶️ Don't switch lights as its day. Now: {} Sunrise: {} Sunset: {}" msgstr "" "🕶️ Schalte Lichter nicht ein, da es Tag ist. Jetzt: {} Sonnenaufgang: {} " "Sonnenuntergang: {}" -#: src/buttons.rs:692 +#: src/buttons.rs:674 msgid "⌛ Timeout with sequence {}" msgstr "⌛ Timeout mit Sequenz {}" -#: src/buttons.rs:703 +#: src/buttons.rs:685 msgid "⌛ Sequence {} too long" msgstr "⌛ Zu lange Sequenz {}" @@ -126,8 +118,12 @@ msgstr "🌡️ Hohe Temperatur {} °C, Fenster schließen (Wind {} m/s)" msgid "🌡️ Heat Alert {} °C, turn on PV cooling (Wind {} m/s)" msgstr "🌡️ Hitzewarnung {} °C, PV Kühlung einschalten (Wind {} m/s)" +<<<<<<< HEAD +#: src/clima_sensor_us.rs:317 +======= #: src/clima_sensor_us.rs:343 #, fuzzy +>>>>>>> master msgid "🌡 Freezing Temperature {} °C, yield is in danger (Wind {} m/s)" msgstr "🌡 Gefrierpunkt um {} °C unterschritten, Ernte in Gefahr (Wind {} m/s)" @@ -158,31 +154,67 @@ msgstr "" msgid "⚠️ Error from weather station: {}" msgstr "⚠️ Ein Fehler ist beim ausführen der Wetterstation aufgetreten: {}" -#: src/environment.rs:394 +#: src/environment.rs:400 msgid "⚠️ Error {:#02b} reading environment! Status: {:#02b}. {}" msgstr "" "⚠️ Fehler {:#02b} beim Lesen vom Environment-Sensor! Status: {:#02b}. {}" -#: src/environment.rs:408 +#: src/environment.rs:414 msgid "💨 Airquality is ok. {}" msgstr "💨 Luftqualität ist ok. {}" -#: src/environment.rs:416 +#: src/environment.rs:422 msgid "💩 Airquality is moderate. {}" msgstr "💩 Luftqualität ist moderat. {}" -#: src/environment.rs:424 +#: src/environment.rs:430 msgid "💩 Airquality is bad! {}" msgstr "💩 Luftqualität ist schlecht! {}" -#: src/environment.rs:437 +#: src/environment.rs:443 msgid "🚨 Possible fire alarm! Ring bell once! ⏰. {}" msgstr "🚨 Mögicher Feuer-Alarm! Läute Glocke 1x! ⏰. {}" -#: src/environment.rs:452 +#: src/environment.rs:458 msgid "🚨 Possible fire alarm! (don't ring yet). {}" msgstr "🚨 Möglicher Feueralarm! (läute noch nicht). {}" +#: src/haustuer.rs:74 +msgid "Board 58 with error {}" +msgstr "Brett 58 mit Fehler {}" + +#: src/haustuer.rs:129 +msgid "💡 Light far outdoor pressed." +msgstr "💡 Licht am Vorgartenzaun gedrückt." + +#: src/haustuer.rs:137 +msgid "💡 Pressed at entrance switch. Switch lights." +msgstr "💡 Knopf beim Vorgartenzaun gedrückt. Schalte Licht ein." + +#: src/haustuer.rs:145 +msgid "🔔 Bell far outdoor pressed." +msgstr "🔔 Taste für Glocke Vorgarten gedrückt." + +#: src/haustuer.rs:156 +msgid "💡 Indoor light pressed." +msgstr "💡 Taster Licht innen gedrückt." + +#: src/haustuer.rs:166 +msgid "💡 Multi-Press in entrance. Switch outdoor lights." +msgstr "💡 Knopf im Vorzimmer mehrmals gedrückt. Schalte Licht aussen ein." + +#: src/haustuer.rs:176 +msgid "💡 Pressed in entrance. Switch indoor lights." +msgstr "💡 Knopf im Vorzimmer gedrückt. Schalte Licht innen ein." + +#: src/haustuer.rs:186 +msgid "⚠️ Error on {}, will do reset." +msgstr "⚠️ Ein Fehler auf {} ist aufgetreten" + +#: src/haustuer.rs:193 +msgid "👋 RST of MOD-IO done." +msgstr "👋 RST von MOD-IO durchgeführt." + #: src/mod_ir_temp.rs:199 msgid "🌡️🌡️ ModIR both sensors too hot! Ambient: {} °C, Object: {} °C" msgstr "🌡️🌡️ Beide ModIR Sensoren sind zu heiß! Ambient: {} °C, Objekt: {} °C" @@ -203,7 +235,7 @@ msgstr "🌡 ModIR warnung wurde entfernt! Ambient: {} °C, Objekt: {} °C" msgid "⚠️ Error while handling ModIR: {}" msgstr "⚠️ Ein Fehler ist bei der Behandlung von ModIR aufgetreten: {}" -#: src/nextcloud.rs:243 +#: src/nextcloud.rs:273 msgid "👋 Opensesame {} init {}" msgstr "👋 Opensesame {} init {}" @@ -215,6 +247,14 @@ msgstr "" "{} Ping! Version {}, {}, Status {}, Error {}, Load {} {} {}, " "Speicherauslastung {}, Swap {}, CPU temp {}, Startup {} Bat {}" +#: src/pwr.rs:75 +msgid "👋 Turned PWR switch OFF" +msgstr "👋 PWR switch AUS geschalten" + +#: src/pwr.rs:84 +msgid "👋 Turned PWR switch ON" +msgstr "👋 PWR switch EIN geschalten" + #: src/sensors.rs:273 msgid "Fire Alarm {}" msgstr "Feueralarm {}" @@ -243,5 +283,11 @@ msgstr "🚨 Befehl für Alarm empfangen" msgid "🔔 Received bell" msgstr "🔔 Befehl für Glocke empfangen" +#~ msgid "👋 Turned PWR_SWITCH off" +#~ msgstr "👋 PWR_SWITCH ausgeschalten" + +#~ msgid "{}" +#~ msgstr "{}" + #~ msgid "{}. {}" #~ msgstr "{}. {}" diff --git a/lang/opensesame.pot b/lang/opensesame.pot index 2659019..d40f5e8 100644 --- a/lang/opensesame.pot +++ b/lang/opensesame.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-09 10:40+0000\n" +"POT-Creation-Date: 2023-12-24 06:54+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -29,83 +29,75 @@ msgstr "" msgid "Couldn't send SIGUSR2 to other opensesame instance: {}" msgstr "" -#: src/bat.rs:60 +#: src/bat.rs:66 msgid "🪫 ON {}%" msgstr "" -#: src/bat.rs:67 +#: src/bat.rs:73 msgid "🪫 Battery Capacity is below {}% at {}%" msgstr "" -#: src/bat.rs:83 +#: src/bat.rs:89 msgid "🔋 ON {}%" msgstr "" -#: src/bat.rs:89 +#: src/bat.rs:95 msgid "🔋 Battery Capacity is back to {}%" msgstr "" -#: src/buttons.rs:496 -msgid "👋 Turned PWR_SWITCH off" -msgstr "" - -#: src/buttons.rs:505 -msgid "👋 Turned PWR_SWITCH on" +#: src/buttons.rs:519 +msgid "{} {}" msgstr "" -#: src/buttons.rs:539 -msgid "{}" -msgstr "" - -#: src/buttons.rs:559 +#: src/buttons.rs:542 msgid "🔔 Pressed button bell." msgstr "" -#: src/buttons.rs:568 +#: src/buttons.rs:551 msgid "🔕 Did not ring bell (button was pressed) because the time 🌜 is {}, {}" msgstr "" -#: src/buttons.rs:580 +#: src/buttons.rs:563 msgid "💡 Pressed switch inside. {}." msgstr "" -#: src/buttons.rs:591 -msgid "💡 Pressed switch outside or light button. {}." +#: src/buttons.rs:574 +msgid "💡 Pressed light button. {}." msgstr "" -#: src/buttons.rs:604 +#: src/buttons.rs:587 msgid "🔔 Pressed switch bell." msgstr "" -#: src/buttons.rs:613 +#: src/buttons.rs:596 msgid "🔕 Did not ring bell (taster outside) because the time 🌜 is {}, {}" msgstr "" -#: src/buttons.rs:627 +#: src/buttons.rs:610 msgid "🕶️ Light was turned off." msgstr "" -#: src/buttons.rs:637 +#: src/buttons.rs:620 msgid "⚠️ Error reading buttons of board {}. Load average: {} {} {}, Memory usage: {}, Swap: {}, CPU temp: {}" msgstr "" -#: src/buttons.rs:650 +#: src/buttons.rs:632 msgid "🤗 Opened for {}" msgstr "" -#: src/buttons.rs:666 +#: src/buttons.rs:648 msgid "💡 Switch lights in and out. {}" msgstr "" -#: src/buttons.rs:676 +#: src/buttons.rs:658 msgid "🕶️ Don't switch lights as its day. Now: {} Sunrise: {} Sunset: {}" msgstr "" -#: src/buttons.rs:692 +#: src/buttons.rs:674 msgid "⌛ Timeout with sequence {}" msgstr "" -#: src/buttons.rs:703 +#: src/buttons.rs:685 msgid "⌛ Sequence {} too long" msgstr "" @@ -145,30 +137,66 @@ msgstr "" msgid "⚠️ Error from weather station: {}" msgstr "" -#: src/environment.rs:394 +#: src/environment.rs:400 msgid "⚠️ Error {:#02b} reading environment! Status: {:#02b}. {}" msgstr "" -#: src/environment.rs:408 +#: src/environment.rs:414 msgid "💨 Airquality is ok. {}" msgstr "" -#: src/environment.rs:416 +#: src/environment.rs:422 msgid "💩 Airquality is moderate. {}" msgstr "" -#: src/environment.rs:424 +#: src/environment.rs:430 msgid "💩 Airquality is bad! {}" msgstr "" -#: src/environment.rs:437 +#: src/environment.rs:443 msgid "🚨 Possible fire alarm! Ring bell once! ⏰. {}" msgstr "" -#: src/environment.rs:452 +#: src/environment.rs:458 msgid "🚨 Possible fire alarm! (don't ring yet). {}" msgstr "" +#: src/haustuer.rs:74 +msgid "Board 58 with error {}" +msgstr "" + +#: src/haustuer.rs:129 +msgid "💡 Light far outdoor pressed." +msgstr "" + +#: src/haustuer.rs:137 +msgid "💡 Pressed at entrance switch. Switch lights." +msgstr "" + +#: src/haustuer.rs:145 +msgid "🔔 Bell far outdoor pressed." +msgstr "" + +#: src/haustuer.rs:156 +msgid "💡 Indoor light pressed." +msgstr "" + +#: src/haustuer.rs:166 +msgid "💡 Multi-Press in entrance. Switch outdoor lights." +msgstr "" + +#: src/haustuer.rs:176 +msgid "💡 Pressed in entrance. Switch indoor lights." +msgstr "" + +#: src/haustuer.rs:186 +msgid "⚠️ Error on {}, will do reset." +msgstr "" + +#: src/haustuer.rs:193 +msgid "👋 RST of MOD-IO done." +msgstr "" + #: src/mod_ir_temp.rs:199 msgid "🌡️🌡️ ModIR both sensors too hot! Ambient: {} °C, Object: {} °C" msgstr "" @@ -189,7 +217,7 @@ msgstr "" msgid "⚠️ Error while handling ModIR: {}" msgstr "" -#: src/nextcloud.rs:243 +#: src/nextcloud.rs:273 msgid "👋 Opensesame {} init {}" msgstr "" @@ -197,6 +225,14 @@ msgstr "" msgid "{} Ping! Version {}, {}, Status {}, Error {}, Load {} {} {}, Memory usage {}, Swap {}, CPU temp {}, Startup {} Bat {}" msgstr "" +#: src/pwr.rs:75 +msgid "👋 Turned PWR switch OFF" +msgstr "" + +#: src/pwr.rs:84 +msgid "👋 Turned PWR switch ON" +msgstr "" + #: src/sensors.rs:273 msgid "Fire Alarm {}" msgstr "" diff --git a/src/bat.rs b/src/bat.rs index a5a565d..f6cfaaf 100644 --- a/src/bat.rs +++ b/src/bat.rs @@ -23,6 +23,12 @@ pub struct Bat { capacity_threshold: u8, } +impl Default for Bat { + fn default() -> Self { + Self::new() + } +} + impl Bat { pub fn new() -> Self { Self { diff --git a/src/buttons.rs b/src/buttons.rs index 73b14c3..43de29b 100644 --- a/src/buttons.rs +++ b/src/buttons.rs @@ -14,17 +14,14 @@ use systemstat::{Platform, System}; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::time::interval; -use tokio::time::sleep; use crate::audio::AudioEvent; use crate::config::Config; use crate::nextcloud::NextcloudChat; use crate::nextcloud::NextcloudEvent; -use crate::pwr::Pwr; use crate::types::ModuleError; use crate::validator::{Validation, Validator}; -use crate::watchdog; pub struct Buttons { pub sequence: Vec, @@ -75,7 +72,7 @@ pub enum CommandToButtons { OpenDoor, RingBell(u32, u32), // maybe implement it with interval RingBellAlarm(u32), - SwitchLights(bool, bool, String), // This also need to implement the sending of a Message to nextcloud, which is now in Garage + SwitchLights(bool, bool, bool, String), // inside, outside, permanent, message } const BELL_MINIMUM_PERIOD: u32 = 20; // = 200ms shortest period time for bell @@ -108,7 +105,7 @@ const RELAY_LICHT_AUSSEN: u8 = 0x01 << 1; const ALL_RELAYS: u8 = RELAY_DOOR | RELAY_LICHT_AUSSEN; -const PINS1_INIT: u8 = 0b01100000; +const PINS1_INIT: u8 = 0b01100000 + 15; // add 15 to avoid "⌛ Timeout mit Sequenz [15]" error // board 21 @@ -118,6 +115,7 @@ pub const BUTTON_LIGHT: u8 = 0x01; pub const BUTTON_BELL: u8 = 0x01 << 1; pub const TASTER_AUSSEN: u8 = BUTTON_LIGHT; +// don't exist anymore, are now in haustuer.rs pub const TASTER_INNEN: u8 = 0x01 << 2; pub const TASTER_GLOCKE: u8 = 0x01 << 3; // = GPIO3 with external pull-up @@ -168,6 +166,7 @@ impl Buttons { self.board20 .smbus_write_byte_data(SET_TRIS, ALL_BUTTONS) .expect("I2C Communication to Buttons does not work"); + // Info: typical point to trigger shutdown via panic self.board21 .smbus_write_byte_data(SET_TRIS, ALL_BUTTONS) .unwrap(); @@ -305,9 +304,7 @@ impl Buttons { let epins1 = self.board20.smbus_read_byte_data(GET_PORTS); if let Err(error) = epins1 { if self.failed_counter > 3 { - self.pins1 = PINS1_INIT; - self.pins2 = PINS2_INIT; - self.init(); + self.do_reset(); return Err(format!("Board 20 with error {}", error)); } self.failed_counter += 1; @@ -317,9 +314,7 @@ impl Buttons { let epins2 = self.board21.smbus_read_byte_data(GET_PORTS); if let Err(error) = epins2 { if self.failed_counter > 3 { - self.pins1 = PINS1_INIT; - self.pins2 = PINS2_INIT; - self.init(); + self.do_reset(); return Err(format!("Board 21 with error {}", error)); } self.failed_counter += 1; @@ -437,8 +432,8 @@ impl Buttons { /// returns what was done /// usually extends light time - /// on double press event (on true) -> make light permanent on (until next press event) - pub fn switch_lights(&mut self, inside: bool, outside: bool) -> String { + /// if permanent==true: on double press event (on true) -> make light permanent on (until next press event) + pub fn switch_lights(&mut self, inside: bool, outside: bool, permanent: bool) -> String { assert!( inside || outside, "logic error, at least one must be switched on!" @@ -463,7 +458,7 @@ impl Buttons { self.light_permanent = false; self.light_timeout = 30; // turn off soon return format!("Light {} not permanent anymore", which); - } else if inside && self.light_timeout > init_light_timeout - 200 { + } else if permanent && self.light_timeout > init_light_timeout - 200 { self.light_permanent = true; ret = "Light now permanently on".to_string(); } else if self.light_timeout > 1 { @@ -480,40 +475,24 @@ impl Buttons { self.board21 .smbus_write_byte_data(SET_RELAYS_ON, RELAY_LICHT_INNEN) .unwrap(); + } else if outside { + self.board21 + .smbus_write_byte_data(SET_RELAYS_ON, RELAY_LICHT_AUSSEN) + .unwrap(); } ret } - async fn do_reset( - nextcloud_sender: Sender, - pwr: &mut Pwr, - ) -> Result<(), ModuleError> { - if pwr.enabled() { - pwr.switch(false); - nextcloud_sender - .send(NextcloudEvent::Chat( - NextcloudChat::Ping, - gettext("👋 Turned PWR_SWITCH off"), - )) - .await?; - sleep(Duration::from_millis(watchdog::SAFE_TIMEOUT)).await; - - pwr.switch(true); - nextcloud_sender - .send(NextcloudEvent::Chat( - NextcloudChat::Ping, - gettext("👋 Turned PWR_SWITCH on"), - )) - .await?; - sleep(Duration::from_millis(watchdog::SAFE_TIMEOUT)).await; - } - Ok(()) + fn do_reset(&mut self) { + self.pins1 = PINS1_INIT; + self.pins2 = PINS2_INIT; + self.init(); + // communication hopefully works again } pub async fn get_background_task( mut self, mut validator: Validator, - mut pwr: Pwr, time_format: String, mut command_receiver: Receiver, nextcloud_sender: Sender, @@ -532,11 +511,15 @@ impl Buttons { CommandToButtons::RingBell(period, counter) => { self.ring_bell(period, counter); } - CommandToButtons::SwitchLights(inside, outside, _text) => { + CommandToButtons::SwitchLights(inside, outside, permanent, text) => { nextcloud_sender .send(NextcloudEvent::Chat( NextcloudChat::Licht, - gettext!("{}", self.switch_lights(inside, outside)), + gettext!( + "{} {}", + self.switch_lights(inside, outside, permanent), + text + ), )) .await?; } @@ -578,7 +561,7 @@ impl Buttons { NextcloudChat::Licht, gettext!( "💡 Pressed switch inside. {}.", - self.switch_lights(true, true) + self.switch_lights(true, true, true) ), )) .await?; @@ -588,8 +571,8 @@ impl Buttons { .send(NextcloudEvent::Chat( NextcloudChat::Licht, gettext!( - "💡 Pressed switch outside or light button. {}.", - self.switch_lights(false, true), + "💡 Pressed light button. {}.", + self.switch_lights(false, true, false), ), )) .await?; @@ -636,7 +619,6 @@ impl Buttons { nextcloud_sender .send(NextcloudEvent::Chat(NextcloudChat::Ping, gettext!("⚠️ Error reading buttons of board {}. Load average: {} {} {}, Memory usage: {}, Swap: {}, CPU temp: {}", board, loadavg.one, loadavg.five, loadavg.fifteen, sys.memory().unwrap().total, sys.swap().unwrap().total, sys.cpu_temp().unwrap()))) .await?; - Buttons::do_reset(nextcloud_sender.clone(), &mut pwr).await?; } } // Validation start @@ -664,7 +646,7 @@ impl Buttons { NextcloudChat::Licht, gettext!( "💡 Switch lights in and out. {}", - self.switch_lights(true, true) + self.switch_lights(true, true, false) ), )) .await?; diff --git a/src/environment.rs b/src/environment.rs index 08a503a..bfd64e2 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -316,7 +316,13 @@ impl<'a> Environment<'a> { Some(board5a) => { // check if we get new data if !self.first_time { - let measurement = self.bme280.as_mut().unwrap().measure().unwrap(); + let measurement = self + .bme280 + .as_mut() + .unwrap() + .measure() + .expect("Communication to Mod-ENV/BME280 does not work"); + // Info: typical point to trigger shutdown via panic set_env_data_ccs811(board5a, measurement.temperature, measurement.humidity); self.temperature = measurement.temperature; self.humidity = measurement.humidity; diff --git a/src/garage.rs b/src/garage.rs index 8b055f6..04d41f3 100644 --- a/src/garage.rs +++ b/src/garage.rs @@ -165,6 +165,7 @@ impl Garage { .send(CommandToButtons::SwitchLights( true, false, + true, "💡 Pressed at entrance top switch. Switch lights in garage" .to_string(), )) @@ -173,6 +174,7 @@ impl Garage { GarageChange::PressedTasterTorOben => { command_sender .send(CommandToButtons::SwitchLights( + true, true, true, "💡 Pressed top switch at garage door. Switch lights in and out garage" diff --git a/src/haustuer.rs b/src/haustuer.rs new file mode 100644 index 0000000..d8b5bf1 --- /dev/null +++ b/src/haustuer.rs @@ -0,0 +1,201 @@ +use futures::never::Never; +use gettextrs::*; +use i2cdev::core::*; +use i2cdev::linux::LinuxI2CDevice; +use i2cdev::linux::LinuxI2CError; +use systemstat::Duration; +use tokio::{sync::mpsc::Sender, time::interval}; + +use crate::{ + buttons::CommandToButtons, + config::Config, + nextcloud::{NextcloudChat, NextcloudEvent}, + rst::Rst, + types::ModuleError, +}; + +const MOD_IO_I2C_ADDR: u16 = 0x58; + +const READ_COMMAND_FOR_IO_OPTO_PINS: u8 = 0x20; + +const IN1: u8 = 0x1; +const IN3: u8 = 0x4; +const IN4: u8 = 0x8; + +#[derive(PartialEq, Debug)] +pub enum HaustuerChange { + None, + + LightFarOutdoor, // IN1 + // IN2 unused + BellFarOutdoor, // IN3 + LightIndoor, // IN4 + + Err(String), +} + +pub struct Haustuer { + board: LinuxI2CDevice, + + light_far_outdoor: bool, + bell_far_outdoor: bool, + light_indoor: bool, + + how_many_err: u8, +} + +impl Haustuer { + pub fn new(_config: &mut Config) -> Self { + Self { + board: LinuxI2CDevice::new("/dev/i2c-2", MOD_IO_I2C_ADDR).unwrap(), + + light_far_outdoor: false, + bell_far_outdoor: false, + light_indoor: false, + + how_many_err: 0, + } + } + + fn _set_relay(&mut self, which: u8) -> Result<(), LinuxI2CError> { + self.board.smbus_write_byte_data(0x10, which)?; + Ok(()) + } + + pub fn handle(&mut self) -> HaustuerChange { + let epins = self + .board + .smbus_read_byte_data(READ_COMMAND_FOR_IO_OPTO_PINS); + if let Err(error) = epins { + self.how_many_err += 1; + if self.how_many_err >= 58 { + panic!("58 errors reached, giving up, last error {}", error) + } + return HaustuerChange::Err(gettext!("Board 58 with error {}", error)); + } + self.how_many_err = 0; + + let pins = epins.unwrap(); + // self.set_relay(pins).unwrap(); + + if pins & IN1 == IN1 { + if !self.light_far_outdoor { + self.light_far_outdoor = true; + return HaustuerChange::LightFarOutdoor; + } + } else { + self.light_far_outdoor = false; + } + + // IN2 unused + + if pins & IN3 == IN3 { + if !self.bell_far_outdoor { + self.bell_far_outdoor = true; + return HaustuerChange::BellFarOutdoor; + } + } else { + self.bell_far_outdoor = false; + } + + if pins & IN4 == IN4 { + if !self.light_indoor { + self.light_indoor = true; + return HaustuerChange::LightIndoor; + } + } else { + self.light_indoor = false; + } + + HaustuerChange::None + } + + pub async fn get_background_task( + mut haustuer: Haustuer, + command_sender: Sender, + nextcloud_sender: Sender, + ) -> Result { + let mut interval = interval(Duration::from_millis(30)); + let mut rst = Rst::new(); + let mut indoor_counter = 100; + loop { + indoor_counter += 1; + match haustuer.handle() { + HaustuerChange::None => (), + HaustuerChange::LightFarOutdoor => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext("💡 Light far outdoor pressed."), + )) + .await?; + command_sender + .send(CommandToButtons::SwitchLights( + false, + true, + false, + gettext("💡 Pressed at entrance switch. Switch lights."), + )) + .await?; + } + HaustuerChange::BellFarOutdoor => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Default, + gettext("🔔 Bell far outdoor pressed."), + )) + .await?; + command_sender + .send(CommandToButtons::RingBell(5, 5)) + .await?; + } + HaustuerChange::LightIndoor => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Licht, + gettext!("💡 Indoor light pressed {}.", indoor_counter), + )) + .await?; + if indoor_counter < 100 { + // multi-click detected (within 3 sec) + command_sender + .send(CommandToButtons::SwitchLights( + false, + true, + false, + gettext("💡 Multi-Press in entrance. Switch outdoor lights."), + )) + .await?; + } else { + // normal click or first click + command_sender + .send(CommandToButtons::SwitchLights( + true, + false, + false, + gettext("💡 Pressed in entrance. Switch indoor lights."), + )) + .await?; + } + indoor_counter = 0; // reset + } + HaustuerChange::Err(err) => { + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext!("⚠️ Error on {}, will do reset.", err), + )) + .await?; + rst.do_reset().await?; + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext("👋 RST of MOD-IO done."), + )) + .await?; + } + } + interval.tick().await; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 4c050bd..f27f599 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,12 @@ pub mod clima_sensor_us; pub mod config; pub mod environment; pub mod garage; +pub mod haustuer; pub mod mod_ir_temp; pub mod nextcloud; pub mod ping; pub mod pwr; +pub mod rst; pub mod sensors; pub mod signals; pub mod ssh; diff --git a/src/main.rs b/src/main.rs index 6ea2b2a..3069894 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,16 @@ use chrono::Local; use futures::future::join_all; use gettextrs::*; use mlx9061x::Error as MlxError; +use std::env; +use std::ops::Deref; use std::panic; +use std::process; use std::sync::Arc; use systemstat::Duration; +use tokio::runtime::UnhandledPanic; use tokio::spawn; use tokio::sync::{mpsc, Mutex}; -use tokio::time::interval; +use tokio::time::interval; // https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.html#method.unhandled_panic use opensesame::audio::{Audio, AudioEvent}; use opensesame::bat::Bat; @@ -16,8 +20,9 @@ use opensesame::clima_sensor_us::ClimaSensorUS; use opensesame::config::Config; use opensesame::environment::{EnvEvent, Environment}; use opensesame::garage::Garage; +use opensesame::haustuer::Haustuer; use opensesame::mod_ir_temp::ModIR; -use opensesame::nextcloud::{Nextcloud, NextcloudChat, NextcloudEvent}; +use opensesame::nextcloud::{try_ping, Nextcloud, NextcloudChat, NextcloudEvent}; use opensesame::ping::{Ping, PingEvent}; use opensesame::pwr::Pwr; use opensesame::sensors::Sensors; @@ -29,11 +34,23 @@ use opensesame::watchdog::Watchdog; const CONFIG_PARENT: &str = "/sw/libelektra/opensesame/#0/current"; const STATE_PARENT: &str = "/state/libelektra/opensesame/#0/current"; -#[tokio::main] -async fn main() -> Result<(), ModuleError> { +fn main() { TextDomain::new("opensesame").init().unwrap(); + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(2) + .unhandled_panic(UnhandledPanic::ShutdownRuntime) + .build() + .unwrap() + .block_on(async { + let _ = start().await; + }) +} +async fn start() -> Result<(), ModuleError> { let mut config = Config::new(CONFIG_PARENT); + env::set_var("RUST_BACKTRACE", config.get::("debug/backtrace")); + let config_mutex = Arc::new(Mutex::new(Config::new(CONFIG_PARENT))); let state_mutex = Arc::new(Mutex::new(Config::new(STATE_PARENT))); @@ -53,6 +70,7 @@ async fn main() -> Result<(), ModuleError> { let buttons_enabled = config.get_bool("buttons/enable"); let garage_enabled = config.get_bool("garage/enable"); + let haustuer_enabled = config.get_bool("haustuer/enable"); let sensors_enabled = config.get_bool("sensors/enable"); let modir_enabled = config.get_bool("ir/enable"); let env_enabled = config.get_bool("environment/enable"); @@ -61,8 +79,44 @@ async fn main() -> Result<(), ModuleError> { let watchdog_enabled = config.get_bool("watchdog/enable"); let ping_enabled = config.get_bool("ping/enable"); + // // https://stackoverflow.com/questions/42456497/stdresultresult-panic-to-log + panic::set_hook(Box::new(|panic_info| { + struct Exit; + impl Drop for Exit { + fn drop(&mut self) { + process::exit(1); + } + } + let _exit = Exit; + + let (filename, line) = panic_info + .location() + .map(|loc| (loc.file(), loc.line())) + .unwrap_or(("", 0)); + let cause = panic_info + .payload() + .downcast_ref::() + .map(String::deref); + let cause = cause.unwrap_or_else(|| { + panic_info + .payload() + .downcast_ref::<&str>() + .map(|s| *s) + .unwrap_or("") + }); + let text = gettext!("A panic occurred at {}:{}: {}", filename, line, cause); + let mut config = Config::new(CONFIG_PARENT); + match try_ping(&mut config, text.clone()) { + Ok(_response) => (), + Err(error) => { + eprintln!("Couldn't set status message {} because {}", text, error); + } + } + })); + let mut tasks = vec![]; + // Step 1: NC tasks.push(spawn(Nextcloud::get_background_task( Nextcloud::new(&mut config), nextcloud_receiver, @@ -72,17 +126,34 @@ async fn main() -> Result<(), ModuleError> { startup_time.to_string(), ))); - if garage_enabled { - if !buttons_enabled { - panic!("Garage depends on buttons!"); - } - tasks.push(spawn(Garage::get_background_task( - Garage::new(&mut config), - command_sender.clone(), - nextcloud_sender.clone(), - ))); + let mut pwr = Pwr::new(&mut config); + nextcloud_sender.send( + NextcloudEvent::Chat(NextcloudChat::Ping, + gettext!("👋 Enabled Modules:\n\tButtons: {},\n\tGarage: {},\n\tHaustür: {},\n\tPWR: {},\n\tSensors: {},\n\tModIR: {},\n\tEnvironment: {},\n\tWeatherstation: {},\n\tBattery: {},\n\tWatchdog: {},\n\tPing: {}\n", + buttons_enabled, + garage_enabled, + haustuer_enabled, + pwr.enabled(), + sensors_enabled, + modir_enabled, + env_enabled, + weatherstation_enabled, + bat_enabled, + watchdog_enabled, + ping_enabled, + ))).await?; + + // Step 2: Watchdog + if watchdog_enabled { + let interval = interval(Duration::from_secs(config.get::("watchdog/interval"))); + let path = config.get::("watchdog/path"); + tasks.push(spawn(Watchdog::get_background_task(path, interval))); } + // Step 3: PWR Reset + let _ = pwr.do_reset(nextcloud_sender.clone()).await; + + // Step 4: Button module (as others require it) if buttons_enabled { let time_format = config.get::("nextcloud/format/time"); let location_latitude = config.get::("location/latitude"); @@ -90,7 +161,6 @@ async fn main() -> Result<(), ModuleError> { tasks.push(spawn(Buttons::get_background_task( Buttons::new(&mut config), Validator::new(&mut config), - Pwr::new(&mut config), time_format.to_string(), command_receiver, nextcloud_sender.clone(), @@ -100,6 +170,29 @@ async fn main() -> Result<(), ModuleError> { ))); } + // Step 5: now other modules, that might communicate with Buttons + if garage_enabled { + if !buttons_enabled { + panic!("⚠️ Garage depends on buttons!"); + } + tasks.push(spawn(Garage::get_background_task( + Garage::new(&mut config), + command_sender.clone(), + nextcloud_sender.clone(), + ))); + } + + if haustuer_enabled { + if !buttons_enabled { + panic!("⚠️ Haustuer depends on buttons!"); + } + tasks.push(spawn(Haustuer::get_background_task( + Haustuer::new(&mut config), + command_sender.clone(), + nextcloud_sender.clone(), + ))); + } + if sensors_enabled { let device_path = config.get::("sensors/device"); tasks.push(spawn(Sensors::get_background_task( @@ -156,7 +249,6 @@ async fn main() -> Result<(), ModuleError> { ))); } - // if env_enabled || buttons_enabled { let audio_bell = config.get::("audio/bell"); let audio_alarm = config.get::("audio/alarm"); tasks.push(spawn(Audio::get_background_task( @@ -164,7 +256,6 @@ async fn main() -> Result<(), ModuleError> { audio_receiver, nextcloud_sender.clone(), ))); - // } if weatherstation_enabled { let clima_sensor_result = ClimaSensorUS::new(&mut config); @@ -194,12 +285,6 @@ async fn main() -> Result<(), ModuleError> { ))); } - if watchdog_enabled { - let interval = interval(Duration::from_secs(config.get::("watchdog/interval"))); - let path = config.get::("watchdog/path"); - tasks.push(spawn(Watchdog::get_background_task(path, interval))); - } - if ping_enabled { tasks.push(spawn(Ping::get_background_task( Ping::new(startup_time.to_string()), @@ -224,19 +309,12 @@ async fn main() -> Result<(), ModuleError> { tasks.push(spawn(signals.get_background_task())); - nextcloud_sender.send( - NextcloudEvent::Chat(NextcloudChat::Ping, - gettext!("Enabled Modules: \nButtons: {},\n Garage: {},\n Sensors: {},\n ModIR: {},\n Environment: {},\n Weatherstation: {},\n Battery: {},\n Watchdog: {},\n Ping: {}\n", - buttons_enabled, - garage_enabled, - sensors_enabled, - modir_enabled, - env_enabled, - weatherstation_enabled, - bat_enabled, - watchdog_enabled, - ping_enabled, -))).await?; + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext("👋Startup Complete"), + )) + .await?; join_all(tasks).await; Ok(()) diff --git a/src/nextcloud.rs b/src/nextcloud.rs index fcb2a01..8732370 100644 --- a/src/nextcloud.rs +++ b/src/nextcloud.rs @@ -46,6 +46,36 @@ pub struct Nextcloud { startup_time: String, } +fn get_headers() -> HeaderMap { + let mut headers = HeaderMap::new(); + headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); + headers.insert(ACCEPT, "application/json".parse().unwrap()); + headers.insert("OCS-APIRequest", "true".parse().unwrap()); + return headers; +} + +pub fn try_ping( + config: &mut Config, + message: String, +) -> Result { + let mut payload = HashMap::new(); + payload.insert("token", config.get::("nextcloud/chat/ping")); + payload.insert("message", message); + reqwest::blocking::Client::new() + .post(format!( + "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}", + config.get::("nextcloud/url"), + config.get::("nextcloud/chat/ping") + )) + .basic_auth( + config.get::("nextcloud/user"), + Some(config.get::("nextcloud/pass")), + ) + .headers(get_headers()) + .json(&payload) + .send() +} + impl Nextcloud { pub fn new(config: &mut Config) -> Self { let mut headers = HeaderMap::new(); @@ -65,7 +95,7 @@ impl Nextcloud { info_environment: String::new(), info_online: String::new(), client, - headers, + headers: get_headers(), startup_time: String::new(), } } @@ -99,12 +129,12 @@ impl Nextcloud { match result { Ok(..) => (), Err(error) => { - eprintln!("Couldn't post to licht {} because {}", message, error); + eprintln!("Couldn't post to light {} because {}", message, error); } }; } - async fn ping(&self, message: String) { + pub async fn ping(&self, message: String) { let result = self.send_message_once(&message, &self.chat_ping).await; match result { @@ -325,7 +355,8 @@ impl Nextcloud { .send(CommandToButtons::SwitchLights( inner_light, outer_light, - String::from("Switch lights {} {}"), + false, // TODO: should be also an argument of switchlights + String::from("Switch light"), )) .await?; } diff --git a/src/pwr.rs b/src/pwr.rs index e1e8ea7..d9cee23 100644 --- a/src/pwr.rs +++ b/src/pwr.rs @@ -1,8 +1,19 @@ +// needs UEXT PIN 3 connected with PWR-SWITCH https://www.olimex.com/Products/Duino/Shields/PWR-SWITCH/ + use gpio_cdev::{Chip, LineHandle, LineRequestFlags}; +use systemstat::Duration; +use tokio::sync::mpsc::Sender; +use tokio::time::sleep; + +use crate::nextcloud::NextcloudChat; +use crate::nextcloud::NextcloudEvent; +use gettextrs::gettext; + use crate::config::Config; +use crate::types::ModuleError; -const GPIO_PWR_LINE: u32 = 202; +const GPIO_PWR_LINE: u32 = 202; // UEXT1 (e.g. LIME-2 Shield) UART4-TX GPIO202 PG10 pub struct Pwr { state: bool, @@ -50,4 +61,30 @@ impl Pwr { } self.state = state; } + + pub async fn do_reset( + &mut self, + nextcloud_sender: Sender, + ) -> Result<(), ModuleError> { + if self.enabled() { + self.switch(false); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext("👋 Turned PWR switch OFF"), + )) + .await?; + sleep(Duration::from_secs(30)).await; + + self.switch(true); + nextcloud_sender + .send(NextcloudEvent::Chat( + NextcloudChat::Ping, + gettext("👋 Turned PWR switch ON"), + )) + .await?; + sleep(Duration::from_secs(10)).await; + } + Ok(()) + } } diff --git a/src/rst.rs b/src/rst.rs new file mode 100644 index 0000000..7ad6d47 --- /dev/null +++ b/src/rst.rs @@ -0,0 +1,54 @@ +// Needs UEXT1 PIN 4 connected with RST of MOD-IO's JTAG PIN 6 (RST) + +use gpio_cdev::{Chip, LineHandle, LineRequestFlags}; + +use systemstat::Duration; +use tokio::time::sleep; + +use crate::types::ModuleError; + +const GPIO_RST_LINE: u32 = 203; // UEXT1 (e.g. LIME-2 Shield) UART4-RX GPIO203 PG11 + +pub struct Rst { + state: bool, + rst_line: LineHandle, +} + +impl Default for Rst { + fn default() -> Self { + Self::new() + } +} + +impl Rst { + pub fn new() -> Self { + let mut chip = Chip::new("/dev/gpiochip0").unwrap(); + let line = chip + .get_line(GPIO_RST_LINE) + .unwrap() + .request(LineRequestFlags::OUTPUT, 0, "gpio_rst_line") + .unwrap(); + Self { + state: line.get_value().unwrap() != 0, + rst_line: line, + } + } + + pub fn switch(&mut self, state: bool) { + if state && !self.state { + self.rst_line.set_value(1).unwrap(); + } else if self.state { + self.rst_line.set_value(0).unwrap(); + } + self.state = state; + } + + pub async fn do_reset(&mut self) -> Result<(), ModuleError> { + self.switch(false); + sleep(Duration::from_millis(1000)).await; + + self.switch(true); + sleep(Duration::from_millis(1000)).await; + Ok(()) + } +} diff --git a/src/signals.rs b/src/signals.rs index 225d749..f92f572 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -204,6 +204,7 @@ impl<'a> Signals<'a> { if self.environment_enabled { self.sigterm().await?; } + panic!("Sigterm received"); } } } diff --git a/src/watchdog.rs b/src/watchdog.rs index ae07a3b..6dce4ea 100644 --- a/src/watchdog.rs +++ b/src/watchdog.rs @@ -4,7 +4,8 @@ use tokio::{fs::File, io::AsyncWriteExt, time::Interval}; use crate::types::ModuleError; -pub const SAFE_TIMEOUT: u64 = 15 * 1000; // safe to wait if trigger was done just before +// irrelevant, as watchdog trigger happens async +// pub const SAFE_TIMEOUT: u64 = 15 * 1000; // safe to wait if trigger was done just before pub struct Watchdog {} diff --git a/src/weather_station/write_to_reg.rs b/src/weather_station/write_to_reg.rs index 298b954..6385f34 100644 --- a/src/weather_station/write_to_reg.rs +++ b/src/weather_station/write_to_reg.rs @@ -5,7 +5,7 @@ use libmodbus::*; use std::env; ///Constants -const DEVICE: &'static str = "/dev/ttyS5"; +const DEVICE: &str = "/dev/ttyS5"; const BAUDRATE: i32 = 9600; const PARITY: char = 'N'; const DATA_BITS: i32 = 8; @@ -31,11 +31,11 @@ fn main() { // open Modbus connection ctx.connect().expect("Verbindung mit ctx Fehlerhaft!"); - ctx.write_registers(KY_REG, 2, &vec![0, 0x1267]) + ctx.write_registers(KY_REG, 2, &[0, 0x1267]) .expect("Error while writing register KY_REG"); ctx.write_registers(reg, 2, &value) .expect("Error while writing register as given by command-line argument"); - ctx.write_registers(KY_REG, 2, &vec![0, 0]) + ctx.write_registers(KY_REG, 2, &[0, 0]) .expect("Error while writing register KY_REG"); // close Modbus connection