SmartRubyPlug 0.1.2.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c9c8f495e70e0f07101a046e10a58eead888fa5ef135002d336dbbee3adcb2ce
4
+ data.tar.gz: 2dc8c5fa4bfa377303fe44902fc32cefcb698e285abe508f4fca5a78013cc2c0
5
+ SHA512:
6
+ metadata.gz: 554cddb7425c5c190c30c5ea2485da11a497f6d1025de4a80212879de1a9d0bb8468834645df18c0609db65466e64bb467f96e12613d75750529e6281c89acd9
7
+ data.tar.gz: 0c549d24ab46fd59454f3e19d939071af4687ef4058124487ef82c06062dc97292419e61560aa595be93c8b600be4eedaad1d4969ea81174a35a5ce66a3721b9
data/Changelog.md ADDED
@@ -0,0 +1,10 @@
1
+ ## 0.1.2.beta
2
+ - fix gemspec
3
+
4
+ ## 0.1.1.beta
5
+ - added Dockerfiles
6
+ - added docker and docker-compose installation doc
7
+ - added doc about how to run the project via Docker
8
+
9
+ ## 0.1.0.beta
10
+ - initial project release
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2022 Martin Markech
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,438 @@
1
+ # Smart Ruby Plug
2
+
3
+ Do you want to reduce monthly energy costs of your Starlink Internet connection?
4
+
5
+ The project has 3 repositories
6
+ - [Matho/smart_ruby_plug](https://github.com/Matho/smart_ruby_plug) - this is main Ruby project
7
+ - [Matho/smart_ruby_plug_c](https://github.com/Matho/smart_ruby_plug_c) - C source code for those, who want to change the drawing on the Raspberry Pi display
8
+ - [Matho/smart_ruby_plug_c_binaries](https://github.com/Matho/smart_ruby_plug_c_binaries) - prebuilded C binaries (.so file) for those, who do not want to build the binary from source code
9
+
10
+ You can find the **demo video** at [this link](http://websupport.matho.sk/smart_plug.webm)
11
+
12
+ ## 1. The motivation behind this project
13
+ Imagine the following situation:
14
+ - you have bought Internet connection from Starlink for your weekend house
15
+ - because it is quite expensive, you are sharing Internet connectivity via wifi with your neighbor, to cut the connectivity costs for you
16
+ - you and your neighbor do not need the connectivity each day 24/7, but mostly during weekends only
17
+ - the problem is, that Starlink is hungry for energy
18
+ - via circular 1st generation dish in standby mode it takes about 40-50W and under the load about 60-70W.
19
+ - when snow melting mode is active, it can probably reach 100W
20
+ - that produce high energy bill in case of 24/7 availability, more then 100 EUR / year
21
+
22
+ So, what we can do with the problem? Simply turn off the Internet connection for the time, when we do not use it and turn it on based on demand.
23
+ It can cut your energy bill dramatically!
24
+
25
+ ### 1.1 Explaining the idea
26
+ The idea is, that you will provide Raspberry Pi with LCD display to your neighbor and he will have option to turn your Starlink dish on, for case it is off (it is scheduled for turning off at midnight for each night)
27
+
28
+ The hardware you need to buy:
29
+ - you have to buy 2x Raspberry Pi (preferred is Raspberry Pi 4B, but you can use any aarch64 supported, like Raspberry Pi 3B and higher) 2GB memory model should be good enough
30
+ - the first one will be used as remote control and the second one as master node with `Home Assistant` smart home software
31
+ - then I have bought [Waveshare 1,3" IPS LCD display HAT 240×240](https://www.waveshare.com/1.3inch-lcd-hat.htm) for the RPI used to act as remote device
32
+ - you need also some 2x MicroSD card, 32GB should be good enough
33
+ - 2x power adapter
34
+ - and the most important is compatible Smart Plug with Home Assistant software. I have bought [TP-LINK Wi-Fi Smart Plug HS100](https://www.tp-link.com/cz/home-networking/smart-plug/hs100/), but it is end-of-life product, so
35
+ you will need to find some another compatible device. Try TP-LINK and check for compatibility with Home Assistant at [https://www.home-assistant.io/integrations/tplink/](https://www.home-assistant.io/integrations/tplink/)
36
+
37
+ ### 1.2 The process is following:
38
+ - you will install the [Home Assistant](https://www.home-assistant.io/installation/raspberrypi/) software on master RPI node
39
+ - then you will be able to turn on/off your Smart Plug, where the Starlink router with dish are plugged in
40
+ - you can activate/deactivate it via the mobile app from `TP-Link`, or via `Home Assistant` software or via REST Api exposed by `Home Assistant`
41
+ - under the hood, this project is using the REST API for communication with the Smart Plug
42
+ - you will provide the second Raspberry PI to your neighbor and it will act as remote control to activate on your Starlink satellite (turn on the Smart Plug)
43
+ - I expect, you have connected houses via WiFi antennas (or via ethernet cable), so both RPIs can communicate in the same network between your houses
44
+ - the end user can control the RPI via small LCD display (by pressing any button)
45
+ - when button is pressed, it will show booting screen
46
+ - when booted, it will switch between on/off/error screens
47
+ - on error screen, the diagnostic info about which node in the network is off are shown
48
+ - this Ruby project is communicating via prebuilded `.so` file in the C-project repository
49
+ - the display drawing and check for key press is written in this associated C project
50
+ - you need to install the Ubuntu OS on the remote control RPIs and install this Ruby projects there
51
+ - test it
52
+
53
+ Note: I'm not going to prepare functionality to turn the Internet off via remote device. (at least not in this MVP) It could produce issues, when you both are connected to the Internet.
54
+ So idea is, that the Smart Plug with Starlink dish will turn off by schedule each night at midnight. And you will turn on the Internet only on demand.
55
+
56
+ ### 1.3 The costs:
57
+ I have identified following costs:
58
+
59
+ - display ... 16 EUR
60
+ - smart plug ... 38 EUR
61
+ - 2x Raspberry Pi 2GB model ... 2x51 EUR
62
+ - 2x power adapter .. 2x10EUR
63
+ - 2x ethernet cable ... 2x1.5EUR
64
+ - 2x micro SD card ... 2x8EUR
65
+ - 2x some rpi case ... optional
66
+ - shipping for orders from eshops ... unknown/optional
67
+
68
+ Total*: 195 EUR
69
+
70
+ **Note:** The yearly energy bill is expected at `430Kwh` It should cost about 100 EUR / year.
71
+ I expect you can sell the used items on bazaar after few years with 70% of buy price,
72
+ so **your initial costs in this case would be only 59 EUR.** Also, you need to include here the saved energy costs.
73
+
74
+ **Note 2:** For the remote device rpi, maybe you could use Raspberry PI Zero 2W. This model is aarch64 compatible. When you use Ubuntu Core distribution, it could be running. But it has not been tested yet.
75
+
76
+ **Note 3:** Currently there is big issue with Raspberry Pi availability. The costs on Ebay are sometimes more then twice the price I'm including on this purchase list.
77
+
78
+ **Note 4:** I recommend some kind of battery like PiJuice HAT to do soft shutdown of operating system.
79
+
80
+ ## 2 Demo video
81
+ You can find the demo video at [this link](http://websupport.matho.sk/smart_plug.webm)
82
+
83
+ **Note:** There is big pause between rendering the screens. This delay is customizable via settings.
84
+
85
+ ## 3. Installation
86
+
87
+ ### 3.1 Home Assistant installation
88
+ Currently (17.7.2022) the Home Assistant software supports Python v3.9+. Because I was running my existing rpi based on Ubuntu 20.04, I needed
89
+ to upgrade system to 22.04 and to upgrade Python to v3.10 package.
90
+
91
+ I have done steps written in this article [https://www.techrepublic.com/article/how-to-upgrade-ubuntu-server-from-20-04-to-22-04/](https://www.techrepublic.com/article/how-to-upgrade-ubuntu-server-from-20-04-to-22-04/)
92
+
93
+ Once your Ubuntu is upgraded to 22.04 we can continue with Home Assistant installation.
94
+ At first, ensure, you have Docker and Docker-compose already installed. The tutorial how to install
95
+ Home Assistant via Docker is at [https://www.home-assistant.io/installation/raspberrypi/#install-home-assistant-container](https://www.home-assistant.io/installation/raspberrypi/#install-home-assistant-container)
96
+
97
+ `mkdir ~/HA`
98
+ `cd ~/HA`
99
+
100
+ `vim docker-compose-ha.yml`
101
+
102
+ Include following:
103
+ ```
104
+ version: '3'
105
+ services:
106
+ homeassistant:
107
+ container_name: homeassistant
108
+ image: "ghcr.io/home-assistant/home-assistant:stable"
109
+ volumes:
110
+ - /home/ubuntu/HA:/config
111
+ - /etc/localtime:/etc/localtime:ro
112
+ restart: unless-stopped
113
+ privileged: true
114
+ network_mode: host
115
+ ```
116
+ Start the docker-compose via:
117
+ `sudo docker-compose -f docker-compose-ha.yml up -d`
118
+
119
+ Restart OS, does it starts after reboot? Navigate in browser to `http://<host>:8123` If you are
120
+ using firewall , open 8123 port via `ufw`
121
+
122
+ We will use wifi-based smart plug, so we do not need to install any Z-wave or Zigbee device integrations.
123
+
124
+ Now we can continue in installation. Next step is to install [Home Assistant Core](https://www.home-assistant.io/installation/raspberrypi/#install-home-assistant-core)
125
+
126
+ Detect the Python version. It should be 3.9+ at the time of writing this tutorial. Home Assistant supports 2 latest minor Python releases, check the info of supported Python version by Home Assistant.
127
+
128
+ ```
129
+ sudo apt-get update
130
+ sudo apt-get upgrade -y
131
+ ```
132
+
133
+ Instal the dependencies via:
134
+ ```
135
+ sudo apt-get install -y python3 python3-dev python3-venv python3-pip libffi-dev libssl-dev libjpeg-dev zlib1g-dev autoconf build-essential libopenjp2-7 libtiff5 libturbojpeg0-dev tzdata
136
+ ```
137
+
138
+ Then create account:
139
+ `sudo useradd -rm homeassistant`
140
+
141
+ Create virtual environment:
142
+ ```
143
+ sudo mkdir /srv/homeassistant
144
+ sudo chown homeassistant:homeassistant /srv/homeassistant
145
+
146
+ sudo -u homeassistant -H -s
147
+ cd /srv/homeassistant
148
+ python3 -m venv .
149
+ source bin/activate
150
+
151
+ python3 -m pip install wheel
152
+ pip3 install homeassistant
153
+
154
+ hass
155
+ ```
156
+ The hass command could take up to 10 minutes.
157
+
158
+ The Home Assistant endpoint is available on the `http://your-ip-here:8123`
159
+ If you are using firewall, open the port via `sudo ufw allow 8123`
160
+ Try reboot, if it auto starts.
161
+
162
+ #### 3.1.1 Home Assistant settings
163
+ Navigate to the profile and generate long-lived token. This token you will use when doing REST queries to Home Assistant REST Api.
164
+ Insert the token to `config/settings.yml`
165
+
166
+ ### 3.2 Smart Plug installation
167
+ Install the `Tplink's` [Kasa Smart](https://play.google.com/store/apps/details?id=com.tplink.kasa_android&hl=sk&gl=US) mobile application and follow the instructions in mobile app.
168
+ After successfull pairing, add the device in Home Assistant.
169
+
170
+ You can check to turn on/off the plug via Home Assistant, or via REST API. The REST APIs doc is at [https://developers.home-assistant.io/docs/api/rest/](https://developers.home-assistant.io/docs/api/rest/)
171
+
172
+ ### 3.3 SmartRubyPlug installation
173
+ You need to install the Ubuntu OS to the rpi device, which will act as remote controller.
174
+
175
+ You can use Raspberry Pi's Imager to prepare bootable micro SD card. Select Ubuntu 22.04 aarch64 for Raspberry Pi 4B.
176
+ Once the card is prepared, insert it into rpi. The default user is ubuntu and password is `ubuntu`. You will be requested to
177
+ change the password immediately. You can set ssh keys to do not require copy paste the password for each login.
178
+
179
+ If you dont want to use ethernet port for internet, but wifi instead, I recommend to check this article [https://arstech.net/raspberry-pi-4-ubuntu-wifi/](https://arstech.net/raspberry-pi-4-ubuntu-wifi/)
180
+
181
+ **The easiest way is install this project via Docker**. See chapter `6. Dockerfile building and running via Docker` for more info.
182
+
183
+ Now we need to install [RVM](https://rvm.io/). RVM is Ruby Version Manager and with RVM you can install multiple Ruby language versions in the same OS and switch between versions easily.
184
+
185
+ Install GPG keys:
186
+ `sudo apt install gnupg2`
187
+ `gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB`
188
+
189
+ Install RVM:
190
+ ```
191
+ curl -sSL https://get.rvm.io | bash -s stable
192
+ ```
193
+ Reload RVM shell for each login:
194
+ ```
195
+ vim ~/.bashrc
196
+ ```
197
+ Before the last `export PATH`, write:
198
+ ```
199
+ source ~/.rvm/scripts/rvm
200
+ ```
201
+ This will activate RVM after each login.
202
+ Logout and login again.
203
+
204
+ Download this SmartRubyPlug project to the home folder, eg to `/home/ubuntu/smart_ruby_plug`. Alternatively, you can use FileZilla to upload code.
205
+ Navigate to `cd ~/smart_ruby_plug`. You should be asked to switch to RVM Ruby version from project. Type `y` to yes and `enter`.
206
+ Install the Ruby version, the RVM is asking to install.
207
+ ```
208
+ rvm install "ruby-3.1.2"
209
+ ```
210
+ This could take few minutes, it will compile Ruby, if no binaries are found.
211
+
212
+ After Ruby is installed, you can install project dependencies.
213
+ `gem install bundler`
214
+ `bundle install`
215
+
216
+ Then you need to copy the prebuilded C `.so` file. This file should be in the following project path: `lib/clibrary/libsmart_plug_C.so`
217
+ After it, you can run the project by executing `bin/smart_ruby_plug start` from the project root.
218
+
219
+ Note: Enable SPI interface
220
+ ```
221
+ sudo apt-get install raspi-config
222
+ sudo raspi-config
223
+ # Choose Interfacing Options -> SPI -> Yes to enable SPI interface
224
+ sudo reboot
225
+ ```
226
+ The display should be working now.
227
+
228
+ ### 3.4 Installing display dependencies and compiling the C source code
229
+ The display redrawing and detection for keypress is written via C code. Then, the `main.so` file is prepared and Ruby is calling the C functions via `FFI` gem.
230
+ If you want to compile the C code instead of using the prebuilded binary, you can follow this steps.
231
+
232
+ **NOTE:** I expect, you need to install `BCM2835 libraries` and `wiringPi libraries` in the following steps also for cases, you would like to run on the prebuilded binary, without custom compilation.
233
+
234
+ The instructions are extracted from [https://www.waveshare.com/wiki/1.3inch_LCD_HAT](https://www.waveshare.com/wiki/1.3inch_LCD_HAT)
235
+
236
+ To be able build the libraries, you need to install:
237
+ ```
238
+ sudo apt-get install gcc cmake
239
+ ```
240
+
241
+ **Install BCM2835 libraries**
242
+
243
+ ```
244
+ cd ~
245
+ wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.68.tar.gz
246
+ tar zxvf bcm2835-1.68.tar.gz
247
+ cd bcm2835-1.68/
248
+ sudo ./configure && sudo make && sudo make check && sudo make install
249
+ #For more details, please refer to http://www.airspayce.com/mikem/bcm2835/
250
+ ```
251
+
252
+ **Install wiringPi libraries**
253
+ ```
254
+ cd ~
255
+ git clone https://github.com/WiringPi/WiringPi
256
+ cd WiringPi
257
+ ./build
258
+ gpio -v
259
+ # Run gpio -v and version 2.70 or newer will appear. If it does not appear, it means that there is an installation error
260
+ ```
261
+
262
+ Because we are not using Python source code examples, we do not need to install Python.
263
+ Also, we do not need to install FBCP driver as we are not displaying the OS GUI screens, but only drawing, like on canvas. (TODO verify, it FBCP driver could improve drawing performance, or not)
264
+
265
+ Download the C project repository code to `~/smart_ruby_plug_c`
266
+
267
+ To build the C project on rpi, you need to install following dependencies:
268
+ ```
269
+ sudo apt-get install gcc doxygen cmake gdb
270
+ ```
271
+
272
+ Build the project via Makefile (or via Clion IDE, if you have configured it correctly):
273
+ ```
274
+ make clean
275
+ make
276
+ ```
277
+ You should see new `main.so` file. You can prepare symlink for Ruby project:
278
+ ```
279
+ ln -s /home/ubuntu/smart_ruby_plug_c/main.so /home/ubuntu/smart_ruby_plug/lib/clibrary/libsmart_plug_C.so
280
+ ```
281
+
282
+ If you are going to do more work with this C project, you can setup `Clion IDE` settings to synchronize target files with your local files in IDE and run
283
+ the building on rpi. The building works only on compatible raspberry pi, it will fail on your localhost. The instruction can be found on [https://www.jetbrains.com/help/clion/remote-projects-support.html](https://www.jetbrains.com/help/clion/remote-projects-support.html)
284
+
285
+ Start the ruby app:
286
+ ```
287
+ cd ~/smart_ruby_plug
288
+ bin/smart_ruby_plug start
289
+ ```
290
+
291
+ **Using custom fonts:**
292
+ Only few fonts and font sizes are available currently. If you want to change the font sizes to your custom, you will need to run this project [https://github.com/zst-embedded/STM32-LCD_Font_Generator](https://github.com/zst-embedded/STM32-LCD_Font_Generator)
293
+ It needs python v3.6. To do not break your current Ubuntu system on your localhost, I recommend to start up new VM for example in `DigitalOcean`.
294
+ If you want to install Python 3.6 on Ubuntu 22.04, you can follow this tutorial [https://stackoverflow.com/questions/72102435/how-to-install-python3-6-on-ubuntu-22-04](https://stackoverflow.com/questions/72102435/how-to-install-python3-6-on-ubuntu-22-04)
295
+
296
+ Then:
297
+ ```
298
+ sudo apt install python3-pip
299
+
300
+ cd STM32-LCD_Font_Generator
301
+ python3.6 -m pip install regex
302
+
303
+ sudo apt-get install libjpeg-dev
304
+ python3.6 -m pip install Pillow
305
+
306
+ wget https://github.com/nascarsayan/fonts-1/raw/master/Menlo.ttc
307
+ // use the custom font size in argument
308
+ python3.6 ../stm32-font.py --font Menlo.ttc --size 42
309
+ ```
310
+ Rewrite the generated file based on the existing C files in project
311
+
312
+ ### 3.5 Start the app on reboot
313
+ Currently, the app is not started on reboot. To do, follow this commands:
314
+
315
+ Create new file:
316
+ ```
317
+ sudo vim /etc/systemd/system/smart_ruby_plug.service
318
+ ```
319
+
320
+ and insert there:
321
+ ```
322
+ [Unit]
323
+ After=
324
+
325
+ [Service]
326
+ ExecStart=/home/ubuntu/smart_ruby_plug/bin/run.sh
327
+
328
+ [Install]
329
+ WantedBy=default.target
330
+ ```
331
+
332
+ Apply changes:
333
+ ```
334
+ sudo systemctl daemon-reload
335
+ sudo systemctl start smart_ruby_plug.service
336
+ ```
337
+
338
+ And auto start on reboot:
339
+ ```
340
+ sudo systemctl enable smart_ruby_plug.service
341
+ ```
342
+
343
+ Check the status via:
344
+ ```
345
+ sudo systemctl status smart_ruby_plug.service
346
+ ```
347
+
348
+ ## 4 Start the app manually (without systemctl)
349
+ Build the `.so` binary on your Raspberry Pi. Then copy the builded file to the `lib/clibrary/libsmart_plug_C.so`
350
+
351
+ Navigate to the root folder of this app and run:
352
+ `bin/smart_ruby_plug start`
353
+
354
+ **Note:** You need to push the button for 2 seconds. Only the light click for few miliseconds doesnt register the key press event.
355
+
356
+ ## 5. Running tests
357
+ Log in to your raspberry pi, `cd` to the project and run:
358
+ `rspec .`
359
+
360
+ The project need the prebuilded `.so` file.
361
+
362
+
363
+ ## 6. Dockerfile building and running via Docker
364
+
365
+ ### 6.1 Install Docker
366
+ ```
367
+ sudo apt-get install \
368
+ apt-transport-https \
369
+ ca-certificates \
370
+ curl \
371
+ gnupg-agent \
372
+ software-properties-common
373
+ ```
374
+
375
+ ```
376
+ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
377
+ ```
378
+ Use proper architecture - `arm64` for > Raspberry Pi 3B or `armhf` for < Raspberry Pi 2B
379
+ ```
380
+ sudo add-apt-repository \
381
+ "deb [arch=arm64] https://download.docker.com/linux/ubuntu \
382
+ $(lsb_release -cs) \
383
+ stable"
384
+ ```
385
+
386
+ ```
387
+ sudo apt-get update
388
+ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose
389
+
390
+
391
+ sudo docker run hello-world
392
+ ```
393
+
394
+ ### 6.2 Build this image
395
+ `cd ~/docker-builds/smart_ruby_plug`
396
+ Pass `--no-cache` for clean build and pass correct url with C binary version for C_BINARY_PATH arg.
397
+
398
+ For aarch64:
399
+ `sudo docker build -t mathosk/smart_ruby_plug:v0.1.0.beta_aarch64 --build-arg ARCH=aarch64 --build-arg C_BINARY_PATH=https://github.com/Matho/smart_ruby_plug_c_binaries/releases/download/v0.1.0.beta/libsmart_plug_C.so.v0.1.0.beta_77865ad7af .`
400
+
401
+ Alternatively for armv7l:
402
+ `sudo docker build -t mathosk/smart_ruby_plug:v0.1.0.beta_armv7l --build-arg ARCH=armv7l --build-arg C_BINARY_PATH=https://github.com/Matho/smart_ruby_plug_c_binaries/raw/master/armv7l_32/v0.1.0/libsmart_plug_C.so.v0.1.0.beta_77865ad7af .`
403
+
404
+ ### 6.3 Execute
405
+ `-d` means detached - running in background. If you do not want to run it in background (for test purposes) remove `-d` option from command line
406
+
407
+ ```
408
+ sudo docker run --privileged -d mathosk/smart_ruby_plug:v0.1.0.beta
409
+ ```
410
+
411
+ **Note:** amd64 arch is not prebuilded, as you need to run this sw on the Raspberry Pi.
412
+
413
+ #### 6.3.1 Docker Compose
414
+ You need to have installed the `docker-compose` package. Check it via:
415
+ `docker-compose --version`
416
+
417
+ Copy `smart_ruby_plug/config/settings.yml` file with modified yml according your needs and upload it to folder
418
+ `/data/smart_ruby_plug/config` on your Raspberry Pi.
419
+
420
+ The docker-compose file is located in this project root. Please, before you start it, point to correct version you want to pull.
421
+
422
+ You can start the project via: (use `-d` for run in background)
423
+ ```
424
+ sudo docker-compose -f docker-compose_aarch64.yml up -d
425
+ ```
426
+
427
+ You can stop it via:
428
+ ```
429
+ sudo docker-compose -f docker-compose_aarch64.yml down
430
+ ```
431
+
432
+ #### 6.3.2 Ram usage
433
+ I have installed Ubuntu 22.04 32bit edition on Raspberry Pi 2B model . This model has 1GB ram.
434
+ In standby mode, without this application, it takes 180MB ram. With this app it is almost 200MB ram usage.
435
+
436
+ ## 7. TODOs
437
+ - add display redrawer specs
438
+ - tag and push to RubyGems, once ready to use it in production
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+
3
+ cd /home/ubuntu
4
+ wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.68.tar.gz
5
+ tar zxvf bcm2835-1.68.tar.gz
6
+ cd bcm2835-1.68/
7
+ ./configure && make && make check && make install
8
+
9
+ cd /home/ubuntu
10
+ git clone https://github.com/WiringPi/WiringPi
11
+ cd WiringPi
12
+ ./build
13
+ gpio -v
data/bin/run.sh ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ source /home/ubuntu/.rvm/scripts/rvm
4
+
5
+ cd /home/ubuntu/smart_ruby_plug; /home/ubuntu/smart_ruby_plug/bin/smart_ruby_plug start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require_relative "../lib/main"
5
+ Main.new.start
6
+
7
+ # Example start
8
+ # bin/smart_ruby_plug start
@@ -0,0 +1,9 @@
1
+ require 'ffi'
2
+
3
+ module CLibrary
4
+ extend FFI::Library
5
+ ffi_lib 'lib/clibrary/libsmart_plug_C.so'
6
+ attach_function :LCD_redraw, [:int, :string, :string, :int, :int], :void
7
+ attach_function :KEY_listen, [:int], :int
8
+ end
9
+
data/lib/main.rb ADDED
@@ -0,0 +1,8 @@
1
+ require_relative "smart_ruby_plug/base"
2
+
3
+ class Main
4
+ def start(given_args = ARGV, config = {})
5
+ ::SmartRubyPlug::Cli.start(ARGV)
6
+ end
7
+ end
8
+
@@ -0,0 +1,23 @@
1
+ require 'require_all'
2
+
3
+ require_relative 'cli'
4
+ require_relative 'config'
5
+ require_relative 'processor'
6
+ require_relative 'stdout_logger'
7
+ require_relative 'version'
8
+ require_relative 'display_redrawer'
9
+
10
+ require_all 'lib/smart_ruby_plug/requests/*.rb'
11
+
12
+ require_relative '../clibrary/library'
13
+
14
+ require 'yaml'
15
+ require 'net/ping'
16
+ require 'httparty'
17
+
18
+ module SmartRubyPlug
19
+ class Base
20
+
21
+ end
22
+ end
23
+
@@ -0,0 +1,14 @@
1
+ require "thor"
2
+
3
+ module SmartRubyPlug
4
+ class Cli < ::Thor
5
+ desc 'start', 'start the SmartRubyPlug app'
6
+ def start
7
+ SmartRubyPlug::StdoutLogger.logger.info("Starting SmartRubyPlug with version #{SmartRubyPlug::Version::VERSION} ...")
8
+
9
+ SmartRubyPlug::Processor.new.process
10
+
11
+ SmartRubyPlug::StdoutLogger.logger.info('Exiting SmartRubyPlug ...')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ module SmartRubyPlug
2
+ class Config
3
+ SETTINGS_FILE = 'config/settings.yml'.freeze
4
+ SETTINGS_FILE_FOR_TEST = 'config/settings.example.yml'.freeze
5
+
6
+ @@settings = nil
7
+
8
+ def self.parsed_settings
9
+ if defined?(RSpec)
10
+ @@settings ||= self.load_yaml(SETTINGS_FILE_FOR_TEST)
11
+ else
12
+ @@settings ||= self.load_yaml(SETTINGS_FILE)
13
+ end
14
+ end
15
+
16
+ def self.load_yaml(file_path)
17
+ YAML.safe_load(File.read(file_path), symbolize_names: true)
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,68 @@
1
+ module SmartRubyPlug
2
+ class DisplayRedrawer
3
+ ON_SCREEN = 1
4
+ OFF_SCREEN = 2
5
+ BOOT_SCREEN = 3
6
+ ERROR_SCREEN = 4
7
+ BLACK_SCREEN = 5
8
+
9
+ def initialize
10
+ @ping_interval = Config.parsed_settings[:ping][:interval] # in seconds
11
+ @boot_countdown_timer = Config.parsed_settings[:timers][:boot_countdown_timer] # in seconds
12
+ @plug_name = Config.parsed_settings[:smart_plug][:name]
13
+ @last_status = nil
14
+ end
15
+
16
+ def redraw_with_error(message)
17
+ redraw_screen(ERROR_SCREEN, message)
18
+ end
19
+
20
+ def redraw_with_status(status)
21
+ case status
22
+ when :on
23
+ redraw_screen(ON_SCREEN)
24
+ when :off
25
+ redraw_screen(OFF_SCREEN)
26
+ when :unavailable
27
+ redraw_screen(ERROR_SCREEN, @plug_name)
28
+ when nil
29
+ redraw_screen(OFF_SCREEN)
30
+ else
31
+ redraw_screen(BLACK_SCREEN)
32
+ end
33
+ end
34
+
35
+ def redraw_screen(screen_id, message = nil)
36
+ status_changed = update_last_status(screen_id, message)
37
+
38
+ if status_changed
39
+ message_lines = [nil, nil]
40
+ message_lines = message.split(' ') if message
41
+
42
+ SmartRubyPlug::StdoutLogger.logger.debug("Redrawing screen with screen_id: #{screen_id} and message_lines: #{message_lines}")
43
+ ::CLibrary.LCD_redraw(screen_id, message_lines[0].to_s, message_lines[1].to_s, @ping_interval, @boot_countdown_timer)
44
+ end
45
+
46
+ # do not listen on key pressed on boot screen to do faster redraw from boot to on screen
47
+ return if screen_id == BOOT_SCREEN
48
+
49
+ exit_code = ::CLibrary.KEY_listen(@ping_interval)
50
+ key_pressed = (exit_code == 1)
51
+
52
+ SmartRubyPlug::StdoutLogger.logger.info("Key was pressed: #{key_pressed}")
53
+
54
+ if key_pressed === true
55
+ SmartRubyPlug::Requests::SmartPlugOnRequest.new.do_request
56
+ redraw_screen(BOOT_SCREEN)
57
+ end
58
+ end
59
+
60
+ def update_last_status(screen_id, message)
61
+ previous_status = @last_status
62
+ new_status = "#{screen_id}_#{message}"
63
+ @last_status = new_status
64
+
65
+ previous_status != new_status
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,69 @@
1
+ module SmartRubyPlug
2
+ class Processor
3
+ def initialize
4
+ @ping_interval = Config.parsed_settings[:ping][:interval] # in seconds
5
+ @check_wifi = Config.parsed_settings[:ping][:check_wifi]
6
+ @check_internet = Config.parsed_settings[:ping][:check_internet]
7
+ @display_redrawer = DisplayRedrawer.new
8
+ end
9
+
10
+ def process
11
+ while true do
12
+ SmartRubyPlug::StdoutLogger.logger.debug('Running processor with ping checks ...')
13
+
14
+ smart_plug_status = smart_plug_check
15
+
16
+ # if the smart plug is on or off, then redraw status
17
+ unless smart_plug_status.nil?
18
+ SmartRubyPlug::StdoutLogger.logger.debug("Smart Plug status: '#{smart_plug_status}'")
19
+
20
+ redraw_display_with_status(smart_plug_status)
21
+ next
22
+ end
23
+
24
+ # when unable to detect smart plug status, the wifi or internet is down. Detect which one and show error
25
+ if @check_wifi
26
+ @wifi_output = wifi_check
27
+ unless @wifi_output == []
28
+ redraw_display_with_error(@wifi_output)
29
+ return
30
+ end
31
+ end
32
+
33
+ if @check_internet
34
+ @internet_output = internet_check
35
+ unless @internet_output == []
36
+ redraw_display_with_error(@internet_output)
37
+ return
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def redraw_display_with_error(hash)
46
+ @display_redrawer.redraw_with_error(hash[:name])
47
+ end
48
+
49
+ # :on, :off, :unavailable or nil
50
+ def redraw_display_with_status(status)
51
+ @display_redrawer.redraw_with_status(status)
52
+ end
53
+
54
+ def wifi_check
55
+ SmartRubyPlug::StdoutLogger.logger.info("Wifi nodes ping check ...")
56
+ SmartRubyPlug::Requests::WifiRequest.new.do_request
57
+ end
58
+
59
+ def internet_check
60
+ SmartRubyPlug::StdoutLogger.logger.info("Internet ping check ...")
61
+ SmartRubyPlug::Requests::InternetRequest.new.do_request
62
+ end
63
+
64
+ def smart_plug_check
65
+ SmartRubyPlug::StdoutLogger.logger.info("Smart Plug ping check ...")
66
+ SmartRubyPlug::Requests::SmartPlugStatusRequest.new.do_request
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,36 @@
1
+ module SmartRubyPlug
2
+ module Requests
3
+ class InternetRequest
4
+ def initialize
5
+ @internet_ping_urls_array = Config.parsed_settings[:ping][:urls][:for_internet]
6
+ @internet_provider_name = Config.parsed_settings[:internet_provider][:name]
7
+ end
8
+
9
+ def do_request
10
+ compute_ping
11
+ end
12
+
13
+ private
14
+
15
+ def compute_ping
16
+ url = random_internet_ping_provider
17
+ net_ping = Net::Ping::External.new(url)
18
+
19
+ if net_ping.ping?
20
+ []
21
+ else
22
+ SmartRubyPlug::StdoutLogger.logger.info("Internet ping - failed ping for '#{@internet_provider_name}'")
23
+
24
+ {
25
+ url: url,
26
+ name: @internet_provider_name
27
+ }
28
+ end
29
+ end
30
+
31
+ def random_internet_ping_provider
32
+ @internet_ping_urls_array.sample
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ module SmartRubyPlug
2
+ module Requests
3
+ class SmartPlugBaseRequest
4
+ def initialize
5
+ init_settings
6
+ end
7
+
8
+ private
9
+
10
+ def init_settings
11
+ ha_node_settings = Config.parsed_settings[:home_assistant_node]
12
+ @home_assistant_host_with_port = ha_node_settings[:host]
13
+ @long_lived_token = ha_node_settings[:long_lived_token]
14
+ @plug_entity_id = ha_node_settings[:smart_plug_entity_id].to_s
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ module SmartRubyPlug
2
+ module Requests
3
+ class SmartPlugOnRequest < SmartPlugBaseRequest
4
+ API_TURN_ON_PATH = '/api/services/switch/turn_on'.freeze
5
+ SUCCESS_API_CODE = 200.freeze
6
+ SUCCESS_API_MESSAGE = 'OK'.freeze
7
+
8
+ def do_request
9
+ response = HTTParty.post(do_request_url,
10
+ body: {
11
+ "entity_id": @plug_entity_id
12
+ }.to_json,
13
+ headers: {
14
+ "content-type" => 'application/json',
15
+ "Authorization" => "Bearer #{@long_lived_token}"
16
+ })
17
+
18
+ response.code == SUCCESS_API_CODE && response.message == SUCCESS_API_MESSAGE
19
+ end
20
+
21
+ def do_request_url
22
+ "#{@home_assistant_host_with_port}#{API_TURN_ON_PATH}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ module SmartRubyPlug
2
+ module Requests
3
+ class SmartPlugStatusRequest < SmartPlugBaseRequest
4
+ SUCCESS_API_CODE = 200.freeze
5
+ API_STATUS_PATH = '/api/states/'.freeze
6
+
7
+ def do_request
8
+ response = HTTParty.get(do_request_url,
9
+ headers: {
10
+ "content-type" => 'application/json',
11
+ "Authorization" => "Bearer #{@long_lived_token}"
12
+ })
13
+
14
+ # request has failed, we dont know if plug is on or off
15
+ return nil unless request_passed?(response)
16
+
17
+ begin
18
+ parsed_body = JSON.parse(response.body)
19
+ rescue => e
20
+ return nil
21
+ end
22
+
23
+ parsed_body['state']&.to_sym
24
+ end
25
+
26
+ def request_passed?(response)
27
+ response.code == SUCCESS_API_CODE
28
+ end
29
+
30
+ def do_request_url
31
+ "#{@home_assistant_host_with_port}#{API_STATUS_PATH}#{@plug_entity_id}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module SmartRubyPlug
2
+ module Requests
3
+ class WifiRequest
4
+ def initialize
5
+ antennas_pings = Config.parsed_settings[:ping][:urls][:for_wifi][:antennas]
6
+
7
+ @client_endpoints = antennas_pings[:client]
8
+ @provider_endpoints = antennas_pings[:provider]
9
+ end
10
+
11
+ # it takes few seconds to execute all pings
12
+ def do_request
13
+ errors = compute_pings(@client_endpoints)
14
+
15
+ # do not compute provider pings, if there are some client errors to, because we show only the first found error
16
+ return errors if errors.length > 0
17
+
18
+ compute_pings(@provider_endpoints)
19
+ end
20
+
21
+ private
22
+
23
+ def compute_pings(array_of_hash_to_check)
24
+ errors = []
25
+
26
+ array_of_hash_to_check.each do |hash|
27
+ net_ping = Net::Ping::External.new(hash[:url])
28
+
29
+ unless net_ping.ping?
30
+ SmartRubyPlug::StdoutLogger.logger.info("Wifi ping - failed ping for '#{hash[:url]}'")
31
+
32
+ errors = hash
33
+ break
34
+ end
35
+ end
36
+
37
+ errors
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ require 'logger'
2
+
3
+ module SmartRubyPlug
4
+ class StdoutLogger
5
+ @@logger = Logger.new(STDOUT)
6
+
7
+ def self.logger
8
+ @@logger
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module SmartRubyPlug
2
+ class Version
3
+ VERSION = "0.1.2.beta".freeze
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ # Encoding: UTF-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require "smart_ruby_plug/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.add_dependency 'thor', '>= 1.2', '< 2'
7
+ spec.add_development_dependency "bundler", ">= 1.0", "< 3"
8
+ spec.authors = ["Martin Markech"]
9
+ spec.description = "Smart Ruby Plug is project to monitor your internet connection and turn on the internet on demand"
10
+ spec.email = "martin.markech@matho.sk"
11
+ spec.files = %w(smart_ruby_plug.gemspec) + Dir["*.md", "bin/*", "lib/**/*.rb"]
12
+ spec.homepage = "https://github.com/Matho/smart_ruby_plug"
13
+ spec.licenses = %w(MIT)
14
+ spec.name = "SmartRubyPlug"
15
+ spec.metadata = {
16
+ "bug_tracker_uri" => "https://github.com/Matho/smart_ruby_plug",
17
+ "changelog_uri" => "https://github.com/Matho/smart_ruby_plug/blob/master/Changelog.md",
18
+ "documentation_uri" => "https://github.com/Matho/smart_ruby_plug/blob/master/README.md",
19
+ "source_code_uri" => "https://github.com/Matho/smart_ruby_plug",
20
+ "wiki_uri" => "https://github.com/Matho/smart_ruby_plug",
21
+ "rubygems_mfa_required" => "false",
22
+ }
23
+ spec.require_paths = %w(lib)
24
+ spec.required_ruby_version = ">= 2.0.0"
25
+ spec.required_rubygems_version = ">= 1.3.5"
26
+ spec.summary = spec.description
27
+ spec.version = SmartRubyPlug::Version::VERSION
28
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: SmartRubyPlug
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2.beta
5
+ platform: ruby
6
+ authors:
7
+ - Martin Markech
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.2'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '3'
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '1.0'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '3'
53
+ description: Smart Ruby Plug is project to monitor your internet connection and turn
54
+ on the internet on demand
55
+ email: martin.markech@matho.sk
56
+ executables: []
57
+ extensions: []
58
+ extra_rdoc_files: []
59
+ files:
60
+ - Changelog.md
61
+ - LICENSE.md
62
+ - README.md
63
+ - bin/install_c_libraries.sh
64
+ - bin/run.sh
65
+ - bin/smart_ruby_plug
66
+ - lib/clibrary/library.rb
67
+ - lib/main.rb
68
+ - lib/smart_ruby_plug/base.rb
69
+ - lib/smart_ruby_plug/cli.rb
70
+ - lib/smart_ruby_plug/config.rb
71
+ - lib/smart_ruby_plug/display_redrawer.rb
72
+ - lib/smart_ruby_plug/processor.rb
73
+ - lib/smart_ruby_plug/requests/internet_request.rb
74
+ - lib/smart_ruby_plug/requests/smart_plug_base_request.rb
75
+ - lib/smart_ruby_plug/requests/smart_plug_on_request.rb
76
+ - lib/smart_ruby_plug/requests/smart_plug_status_request.rb
77
+ - lib/smart_ruby_plug/requests/wifi_request.rb
78
+ - lib/smart_ruby_plug/stdout_logger.rb
79
+ - lib/smart_ruby_plug/version.rb
80
+ - smart_ruby_plug.gemspec
81
+ homepage: https://github.com/Matho/smart_ruby_plug
82
+ licenses:
83
+ - MIT
84
+ metadata:
85
+ bug_tracker_uri: https://github.com/Matho/smart_ruby_plug
86
+ changelog_uri: https://github.com/Matho/smart_ruby_plug/blob/master/Changelog.md
87
+ documentation_uri: https://github.com/Matho/smart_ruby_plug/blob/master/README.md
88
+ source_code_uri: https://github.com/Matho/smart_ruby_plug
89
+ wiki_uri: https://github.com/Matho/smart_ruby_plug
90
+ rubygems_mfa_required: 'false'
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 2.0.0
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">"
103
+ - !ruby/object:Gem::Version
104
+ version: 1.3.1
105
+ requirements: []
106
+ rubygems_version: 3.3.7
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Smart Ruby Plug is project to monitor your internet connection and turn on
110
+ the internet on demand
111
+ test_files: []