29. 9. 2020

Responsive Typography

h1 {
  font-size: calc(2em * var(--text-multiplier, 1));
}

p {
  font-size: calc(1em * var(--text-multiplier, 1));
}

@media (min-width: 48rem) {
  :root {
    --text-multiplier: 1.25;
  }
}

#css #webdesign #typography

24. 9. 2020

Fetch in pure Node.js

There are a lot of popular Node.js modules for requestiong data with HTTP or HTTPS (e.g. axios, got, node-fetch), but you don't need them! You can easily made HTTP/HTTPS requests in Node.js with http.get or https.get:

import https from "https";

module.exports = (params, postData) => new Promise((resolve, reject) => {
    const req = https.request(params, (res) => {

        // reject on bad status
        if (res.statusCode < 200 || res.statusCode >= 300) {
            return reject(new Error('statusCode=' + res.statusCode));
        }

        // read data
        let body = [];
        res.on('data', chunk => {
            body.push(chunk);
        });

        res.on('end', () => {
            try {
                body = Buffer.concat(body).toString();
            } catch (e) {
                reject(e);
            }

            resolve(body); // submit data
        });
    });

    req.on('error', (err) => {
        reject(err);
    });

    if (postData) {
        req.write(postData);
    }

    req.end(); // close request
});

Then just call this:

import fetch from './fetch'

(async () => {
  const content = await fetch('https://example.com');
  console.log(content);
})();

Happy nodescripting!

#javascript #nodejs

21. 9. 2020

Read Temperature and Huminidy from MHO-C401

MHO-C401 is new (2020) MMC E-Ink Screen Smart Bluetooth Thermometer Hygrometer BT2.0 Temperature Humidity Sensor from Xiaomi. You can order yours on Gearbest or Aliexpress.

MHO-C401
MHO-C401 Bluetooth Thermometer Hygrometer

Searching for sensor

Every Bluetooth Low Energy (BLE) device have unique MAC address - you can search this address with hcitool

hcitool is Linux tool for monitoring and configuring Bluetooth devices. It is aptly named hcitool as it communicates via a common HCI (Host Controller Interface port to your bluetooth devices. You can utilize the utility to scan for devices and send commands/data for standard Bluetooth and Bluetooth Low Energy.

First check, if your hcitool can see your device with hcitool dev command, then you can start lescan for other devices arroud.

sudo hcitool lescan

You will get output similar to this one:

49:01:7D:E8:21:CF (unknown)
4D:E8:5B:D3:56:7E (unknown)
5C:6D:23:6F:57:14 (unknown)
...
A4:C1:38:4B:E8:FF (unknown)
A4:C1:38:4B:B7:FF MHO-C401
...

As you can see from list my MHO-C401 have A4:C1:38:4B:B7:FF MAC address.

Lets read some data!

We will use a Python to read the data from BLE - there are some great libraries for that, like bluepy. Bluepy provide a comprehensive API to allow access to Bluetooth Low Energy devices.

sudo apt-get install python3-pip libglib2.0-dev
sudo pip3 install bluepy

Each BLE devices provide Services and Characteristics. Services are used to break data up into logic entities, and contain specific chunks of data called characteristics. A service can have one or more characteristics, and each service distinguishes itself from other services by means of a unique numeric ID called a UUID, which can be either 16-bit (for officially adopted BLE Services) or 128-bit (for custom services).

The Python code below will generate a list of all the available services and characteristics on the our BLE device.

from bluepy import btle
device = btle.Peripheral()
try:
  print("Connecting to device...")
  device.connect("A4:C1:38:4B:B7:FF") # need change your MAC address here 
  for service in device.getServices():
    print(str(service))
    for ch in service.getCharacteristics():
        if ch.supportsRead():
              print(" {}".format(ch))
            print(" > UUID: ", ch.uuid)
            print(" > HANDLE: ", hex(ch.getHandle()))
            print(" > SUPPORTS: ", ch.propertiesToString())
            if (ch.supportsRead()):
              try:
                print(" > RESULTS ", repr(ch.read()))
              except BTLEException as e:
                print(" > ERROR: ", e)
            print()
finally:
  device.disconnect()

Output will looks like that:

Connecting to device...
Service <uuid=Generic Access handleStart=1 handleEnd=7>
 Characteristic <Device Name>
 > UUID: 00002a00-0000-1000-8000-00805f9b34fb
 > HANDLE: 0x2
 > SUPPORTS: READ NOTIFY 
 > RESULTS  b'MHO-C401\x00'

 Characteristic <Appearance>
 > UUID: 00002a01-0000-1000-8000-00805f9b34fb
 > HANDLE: 0x4
 > SUPPORTS: READ 
 > RESULTS  b'\x00\x00'

 Characteristic <Peripheral Preferred Connection Parameters>
 > UUID: 00002a04-0000-1000-8000-00805f9b34fb
 > HANDLE: 0x6
 > SUPPORTS: READ 
 > RESULTS  b'\x14\x00(\x00\x00\x00\xe8\x03'

...

Then you can read first device characteristic, with follow code:

from bluepy import btle

device = btle.Peripheral()

try:
  device.connect("A4:C1:38:4B:B7:FF")

  # read by UUID
  deviceName = device.getCharacteristics(uuid="00002a00-0000-1000-8000-00805f9b34fb")[0].read()
  print("Device name: ", deviceName.decode('ascii'))

  # or by handle  
  print("Firmware: " , device.readCharacteristic(0x12).decode('ascii'))
  print("Hardware Revision: " , device.readCharacteristic(0x14).decode('ascii'))
  print("Manufacturer Name: " , device.readCharacteristic(0x18).decode('ascii'))

  # read battery level
  print("Battery level: {}%".format(ord(device.readCharacteristic(0x1b))))

  # read device units
  if (device.readCharacteristic(0x33) == b'\x00'):
    print('Units: °C')

  if (device.readCharacteristic(0x33) == b'\x01'):
    print('Units: °F')

finally:
  device.disconnect()

Reading temperature and humidity

For reading temperature and humidity you have to subscribe notifications for UUID = EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6 - there are 3 bytes of data. Notifications are processed by creating a “delegate” object and registering it with the Peripheral.

from bluepy import btle
from datetime import datetime

device = btle.Peripheral()

class Delegate(btle.DefaultDelegate):
  def handleNotification(self, cHandle, data):    
    temperature_bytes = data[:2]
    humidity_bytes = data[2]
    temperature = int.from_bytes(temperature_bytes, byteorder="little") / 100.0
    humidity = humidity_bytes

    print("Tempterature: {}°C".format(temperature))
    print("    Humidity: {}%".format(humidity))
    print("        Time: {}".format(datetime.now().strftime("%H:%M:%S")))

try:  
  device.connect("A4:C1:38:4B:B7:FF")
  device.setDelegate(Delegate())
  ch = device.getCharacteristics(uuid="EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6")[0]
  desc = ch.getDescriptors(forUUID=0x2902)[0]
  desc.write(0x01.to_bytes(2, byteorder="little"), withResponse=True)

  # waiting to notification
  while True:
    if not device.waitForNotifications(5.0):
      break

finally:
  device.disconnect()

Upper code will generate follow output:

Tempterature: 25.82°C
    Humidity: 49%
        Time: 13:32:09

Source codes https://github.com/OzzyCzech/MHO-C401

Sources

#iot #bluetooth

23. 7. 2020

Advanced tasks with Homebrew

Install specific cask version

cd /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask

Then show history of selected formula like:

git log master -- Casks/phpstorm.rb

and you will get hashes of each commit (change) that was made in this formula:

commit d49e3f36f3c7844c580d04d46a71cfd5d10f56c3
Author: jcbot <jcb@leipert.io>
Date:   2020-07-22

    Update phpstorm to 2020.1.4,201.8743.18 (#86377)

commit fe7ff338780dd1a4ebb14086ca0c82d8e865874b
Author: jcbot <jcb@leipert.io>
Date:   2020-07-08

    Update phpstorm to 2020.1.3,201.8538.41 (#85574)

commit 7476855299dc91eae6d72e1018900783e3c56a8b
Author: jcbot <jcb@leipert.io>
Date:   2020-06-03

    Update phpstorm to 2020.1.2,201.7846.90 (#83674)

commit 2c625cbfbd167829b6e607106fbcbac636ac2eb7
Author: jcbot <jcb@leipert.io>
Date:   2020-04-30

    Update phpstorm to 2020.1.1,201.7223.96 (#81664)

Then you can install or reinstall selected version like:

brew cask reinstall https://raw.githubusercontent.com/caskroom/homebrew-cask/2c625cbfbd167829b6e607106fbcbac636ac2eb7/Casks/phpstorm.rb

Switch Homebrew all formulas to older version

Sometimes you just won’t be able install latest version of any program and need some older (e.g. PHP 5.6.1 won’t work and you need install at least 5.6.0 and getting your work done). As you know all brew formulas are GIT repos, you can swith to older version easly:

cd /usr/local/Homebrew/Library/Taps/homebrew

There are homebrew-cask, homebrew-core and homebrew-services dirs:

cd homebrew-core
git log --pretty="%h - %s" -10
71b2069 - Update to PHP 5.5.18
b8aeb54 - Use homebrew's openssl for IMAP
f0d721a - php56: improve phpdbg logic
0dc3f1c - Update to PHP 5.6.1
908fedd - Update to Blitz 0.8.12
4801697 - Updates formula for WP CLI  to version 0.17.0
00560f3 - Upgrade php*-swoole to 1.7.5
8cbd369 - Updated PHP_CodeSniffer
a81eba6 - update pecl_http to 2.1.2
6a88856 - Add HEAD url for composer

Then just switch

git checkout 908fedd

And then run brew install php56 and older version PHP will be installed. Procedure can be used for any formulas.

Reset everything back

PS: you can always reset everything back to normal with follow command

brew update-reset

#brew #macOS

28. 4. 2020

Minimalistics CSS

https://andybrewer.github.io/mvp/

https://picnicss.com/

ffmpeg -i "https://server.com/index.m3u8" -c copy -bsf:a aac_adtstoasc "output.mp4"

#css #webdesign

20. 4. 2020

Backup mongo indexes

There is really short and briliant script for create backup of indexes queries. This code iterate over all collections and create backup of createIndex() queries.

print(`// Backup indexes of : ${db.getName()} : database`);
print(`use ${db.getName()};`);

db.getCollectionNames().forEach(function (collection) {
    indexes = db.getCollection(collection).getIndexes().forEach(function (index) {
        if (index.name === '_id_') return; // skip defalut _id indexes
        const keys = tojsononeline(index.key);
        delete index.id; delete index.key; delete index.v; delete index.ns;
        print(`db.${collection}.createIndex(${keys}, ${tojsononeline(index)});`);
    });
});

You can save this backup code to file and run int directly with mongoshell command:

mongo --quiet mongodb://localhost:27017/mydb ./backup.indexes.js > myindexes.js

Example output:

db.users.createIndex({"settings" : 1}, {"name" : "settingsIndex", "background" : true});
db.users.createIndex({"name" : 1}, {"name" : "nameIndex", "background" : true});
db.users.createIndex({"email" : 1}, {"name" : "emailIndex", "background" : true});

#mongo

7. 4. 2020

Install mongo on macOS

First you need tap official mongo brew:

brew tap mongodb/brew

Then just install mongo with:

 brew install mongodb-community

There is one tool which people often want to install on its own, and that’s the MongoDB shell.

brew install mongodb-community-shell

You can also install only tools for managing mongo database:

brew install mongodb-database-tools

Starting mongo

brew services start mongodb-community

or stop

brew services stop mongodb-community

#brew #macOS #mongo