HTML Skeleton

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
  
    <title></title>
  
    <meta name="description" content=""/>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
    <meta name="author" content=""/>

    <!-- OG -->
    <meta property="og:title" content=""/>
    <meta property="og:site_name" content=""/>
    <meta property="og:description" content=""/>
    <meta property="og:image" content=""/>
    <meta property="og:url" content=""/>

    <!-- Twitter card -->
    <meta name="twitter:card" content="summary_large_image"/>
    <meta name="twitter:image:alt" content="image description"/>

    <!-- Sitemap & RSS feed -->
    <link rel="sitemap" type="application/xml" title="Sitemap" href="/sitemap.xml"/>
    <link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml"/>

    <!-- Tailwind -->
    <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
    <link href="style.css" rel="stylesheet"/>

    <!-- Favicon -->
    <link rel="icon" type="image/svg+xml" href="/favicon.svg"/>
</head>
<body class="font-sans text-gray-900 leading-normal tracking-normal grid min-h-screen">
  <header></header>
  <aside></aside>
  <main class="grid items-center order-first">
  </main>
  <footer></footer>
  <script src="app.js" async></script>
</body>
</html>

With:

#HTML#Snippets#2020

Yarn tips and tricks

Chose Yarn version

Let’s find out your current Yarn version with yarn --version. If your local Yarn version is 1.22 and above go ahead and type-in:

yarn set version berry

This should fetch Yarn 2 and show you an output as follows:

Resolving berry to a url...
Downloading https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js...
Saving it into /Volumes/Work/web/.yarn/releases/yarn-berry.cjs...
Updating /Volumes/Work/web/.yarnrc.yml...
Done!

You can downgrade back with follow:

yarn set version 1.22.5

Upgrade all npm packages interactively

This is similar to npm-check interactive update mode. It provides an easy way to update outdated packages

yarn upgrade-interactive --latest 

Will looks like follow:

[1/? Choose which packages to update. (Press <space> to select, <a> to toggle all, <i> to inverse selection)

 devDependencies
❯◯ autoprefixer      6.7.7  ❯  7.0.0          https://github.com/postcss/autoprefixer#readme
 ◯ webpack           2.4.1  ❯  2.5.1          https://github.com/webpack/webpack

 dependencies
 ◯ bull              2.2.6  ❯  3.0.0-alpha.3  https://github.com/OptimalBits/bull#readme
 ◯ fs-extra          3.0.0  ❯  3.0.1          https://github.com/jprichardson/node-fs-extra
 ◯ socket.io         1.7.3  ❯  1.7.4          https://github.com/socketio/socket.io#readme
 ◯ socket.io-client  1.7.3  ❯  1.7.4          https://github.com/Automattic/socket.io-client#readme

Offline package mirrors

Yarn can maintain offline copies of your packages for more repeatable and reliable build. Create .yarnrc file with follow content:

yarn-offline-mirror "./.npm"
yarn-offline-mirror-pruning true
--install.prefer-offline true
--install.dev true

All npm packages will be saved to .npm folder locally.

#yarn#npm#javascript#2020

October 2020

Apps & services

  • Notion - All-in-one workspace - one tool for your whole team. Write, plan, and get organized.
  • BionicReading - With the Bionic Reading© API you can deliver the next level of reading to your audience.
  • There is a new version of Reeder with iCloud sync and more.

From GitHub

  • Nextra - Nextra is a Next.js and Markdown (MDX) based site generator. 0 line of code needed.
  • wp-latte - This mu-plugin gives theme and plugin developers availability to write templates with Nette Latte v2.5.
  • PHP CS Fixer - The PHP Coding Standards Fixer (PHP CS Fixer) tool fixes your code to follow standards; whether you want to follow PHP coding standards as defined in the PSR-1, PSR-2, etc., or other community driven ones like the Symfony one. You can also define your (team's) style through configuration.

Javascript editors

  • Editor.js Next generation block styled editor. Free. Use for pleasure.
  • EasyMDE - Markdown Editor - A simple, beautiful, and embeddable JavaScript Markdown editor. (fork of SimpleMDE)
  • SimpleMDE - A simple, beautiful, and embeddable JavaScript Markdown editor.
  • Slate - editor framework

Design

  • Cascadia Code Font - This is a fun, new monospaced font that includes programming ligatures and is designed to enhance the modern look and feel of the Windows Terminal.
  • FiraCode - Free monospaced font with programming ligatures

IPFS

Exploring IPFS a peer-to-peer hypermedia protocol designed to make the web faster, safer, and more open. IPFS powers the Distributed Web https://ipfs.io/

brew cask install ipfs

Logitech Folio Touch for iPad Air (4tg gen)

Logitech Folio Touch Backlit keyboard case with trackpad for iPad Pro® 11-inch and iPad Air® (4th gen)

Logitech Folio Touch for iPad Air (4th gen)
Logitech Folio Touch for iPad Air (4th gen)

#notes#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#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#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#2020