Simple-Jekyll-Search

Build Status

A JavaScript library to add search functionality to any Jekyll blog.


idea from this blog post


Promotion: check out Pomodoro.cc

Demo

Getting started

  • Place the following code in a file called search.json in the root of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:
---
---
[
  
    {
      "title"    : "How to trigger a Node-RED flow from the Home Assistant UI",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/how-to-trigger-a-node-red-flow-from-the-home-assistant-ui/",
      "date"     : "2019-07-14 06:30:00 -0400"
    } ,
  
    {
      "title"    : "Smartening up a dumb light switch to control smart bulbs",
      "category" : "",
      "tags"     : "HomeAssistant, ESPHome, LIFX, Shelly",
      "url"      : "/smartening-up-a-dumb-light-switch-to-control-smart-bulbs/",
      "date"     : "2019-06-01 06:30:00 -0400"
    } ,
  
    {
      "title"    : "Making Home Assistant's Presence Detection not so Binary (Node-RED version)",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/making-home-assistants-presence-detection-not-so-binary-node-red-version/",
      "date"     : "2018-12-25 05:30:00 -0500"
    } ,
  
    {
      "title"    : "[Lovelace] Accessing Home Assistant states page with just one click",
      "category" : "",
      "tags"     : "HomeAssistant, Lovelace",
      "url"      : "/lovelace-accessing-home-assistant-states-page-with-just-one-click/",
      "date"     : "2018-12-24 05:30:00 -0500"
    } ,
  
    {
      "title"    : "How to install Hass.io on Ubuntu Server 18.04",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/how-to-install-hass.io-on-ubuntu-server-18.04/",
      "date"     : "2018-11-17 05:30:00 -0500"
    } ,
  
    {
      "title"    : "How to run a Node-RED flow on Home Assistant start",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/make-a-node-red-flow-run-on-home-assistant-start/",
      "date"     : "2018-07-09 08:00:00 -0400"
    } ,
  
    {
      "title"    : "How to hide offline Steam sensor entities in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/how-to-hide-offline-steam-sensor-entities-in-home-assistant/",
      "date"     : "2018-07-09 06:00:00 -0400"
    } ,
  
    {
      "title"    : "Using Node-RED to capture Dash Button press",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/using-node-red-to-capture-dash-button-press/",
      "date"     : "2018-05-28 16:30:00 -0400"
    } ,
  
    {
      "title"    : "Track battery levels with Home Assistant and Custom UI",
      "category" : "",
      "tags"     : "HomeAssistant, CustomUI",
      "url"      : "/track-battery-levels-with-home-assistant-and-custom-ui/",
      "date"     : "2018-04-25 07:25:00 -0400"
    } ,
  
    {
      "title"    : "Dynamically hide/unhide an entire view in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/dynamically-hide-unhide-an-entire-view-update/",
      "date"     : "2018-04-19 07:25:00 -0400"
    } ,
  
    {
      "title"    : "Use an IP phone to 'say' Home Assistant notifications",
      "category" : "",
      "tags"     : "HomeAssistant, FreePBX, Asterisk",
      "url"      : "/use-an-ip-phone-to-say-home-assistant-notifications/",
      "date"     : "2018-04-10 12:05:00 -0400"
    } ,
  
    {
      "title"    : "How I used Caddy Proxy to publish this blog before migrating it to GitHub Pages",
      "category" : "",
      "tags"     : "HomeAssistant, DuckDNS, CaddyProxy, GitHub",
      "url"      : "/how-i-used-caddy-proxy-to-publish-this-blog-before-migrating-it-to-github-pages/",
      "date"     : "2018-04-04 06:40:00 -0400"
    } ,
  
    {
      "title"    : "Blink lights when your IP phone rings",
      "category" : "",
      "tags"     : "HomeAssistant, FreePBX, Asterisk",
      "url"      : "/blink-lights-when-your-ip-phone-rings/",
      "date"     : "2018-03-21 11:48:00 -0400"
    } ,
  
    {
      "title"    : "Managing groups visibility in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/managing-groups-visibility-in-home-assistant/",
      "date"     : "2018-03-18 15:54:00 -0400"
    } ,
  
    {
      "title"    : "Dynamically hide/unhide an entire view in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/dynamically-hide-unhide-an-entire-view/",
      "date"     : "2018-03-15 17:03:00 -0400"
    } ,
  
    {
      "title"    : "3 different ways to group light entities in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/three-different-approaches-for-grouping-light-entities-in-home-assistant/",
      "date"     : "2018-03-12 17:00:00 -0400"
    } ,
  
    {
      "title"    : "Hello World",
      "category" : "",
      "tags"     : "",
      "url"      : "/hello-world/",
      "date"     : "2018-03-12 09:00:00 -0400"
    } 
  
]
  • configure the library ( options )

Note that the index generated in search.json does not include the posts’ content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts’ content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add

"content"  : ""

to search.json after the "date" line to which you must add a comma (,).

Install with bower

bower install simple-jekyll-search

Setup

You need to place the following code within the layout where you want the search to appear.

For example in _layouts/default.html:

<!-- Html Elements for Search -->
<div id="search-container">
<input type="text" id="search-input" placeholder="search...">
<ul id="results-container"></ul>
</div>

<!-- Script pointing to jekyll-search.js -->
<script src="/bower_components/simple-jekyll-search/dest/jekyll-search.js" type="text/javascript"></script>

Options

Customize SimpleJekyllSearch by passing in your configuration options:

SimpleJekyllSearch({
  searchInput: document.getElementById('search-input'),
  resultsContainer: document.getElementById('results-container'),
  json: '/search.json',
})

The above initialization needs to occur after the inclusion of jekyll-search.js.

searchInput (Element) [required]

The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.

resultsContainer (Element) [required]

The container element in which the search results should be rendered in. Typically an <ul>.

json (String|JSON) [required]

You can either pass in an URL to the search.json file, or the results in form of JSON directly, to save one round trip to get the data.

searchResultTemplate

The template of a single rendered search result.

The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.

E.g.

The template

<li><a href="{url}">{title}</a></li>

will render to the following

<li><a href="/jekyll/update/2014/11/01/welcome-to-jekyll.html">Welcome to Jekyll!</a></li>

If the search.json contains this data

[

    {
      "title"    : "Welcome to Jekyll!",
      "category" : "",
      "tags"     : "",
      "url"      : "/jekyll/update/2014/11/01/welcome-to-jekyll.html",
      "date"     : "2014-11-01 21:07:22 +0100"
    }

]

noResultsText

The HTML that will be shown if the query didn’t match anything.

limit

You can limit the number of posts rendered on the page.

fuzzy

Enable fuzzy search to allow less restrictive matching.

exclude

Pass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).

Enable full content search of posts and pages

  • Replace ‘search.json’ with the following code:
---
layout: null
---
[
  
    {
      "title"    : "How to trigger a Node-RED flow from the Home Assistant UI",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/how-to-trigger-a-node-red-flow-from-the-home-assistant-ui/",
      "date"     : "2019-07-14 06:30:00 -0400",
      "content"  : "I see a lot of people who use Node-RED as their automation engine looking for a way to trigger a flow from the Home Assistant UI. Here’s how I do it.First I created an empty script.script:  good_morning:    sequence:And a button in Lovelace.- type: entity-button  entity: script.good_morning  name: "Good Morning"  icon: mdi:coffee  tap_action:    action: call-service    service: script.turn_on    service_data:      entity_id: script.good_morningThen in Node-RED.The events: all node checks only events of type call_service.The switch node checks which script was called.The call-service node turns on my bedroom ceiling lights in a 30 seconds transition.When I press the button (below) in the UI or say “Hey Google, good morning” 1 the flow is triggered and the lights turn on.I prefer this approach because I do not like the idea of using input_boolean for this. It would be like using a light switch to activate a doorbell.            The script was exposed to Google Assistant using Home Assistant Cloud. &#8617;      "
    } ,
  
    {
      "title"    : "Smartening up a dumb light switch to control smart bulbs",
      "category" : "",
      "tags"     : "HomeAssistant, ESPHome, LIFX, Shelly",
      "url"      : "/smartening-up-a-dumb-light-switch-to-control-smart-bulbs/",
      "date"     : "2019-06-01 06:30:00 -0400",
      "content"  : "I see a lot of people having issues and/or doubts about the light switches when installing smart bulbs in their homes. This is how I do it in my house.As I mentioned in a previous post, I have several LIFX installed in the house. And, using only the original switches with them was out of the question. If a switch were turned off, the functionalities of the bulbs connected to that circuit would be lost.On the other hand, I had to keep the switches for my older relatives (and some visitors), who always come here, and are used to using the “good old switches” or are not comfortable using voice assistants.At first, I tried to use Amazon Dash Buttons while I kept the light button on. It worked very well, but the time between pushing the button and the light turn on was a bit annoying. Not to mention the complaints of those who used them: “Why do your lights take so long to turn on?”. Sometimes it would take almost 3 seconds for a light to come on.I also tried replacing the switches with TP-Link HS200 Smart Switches. They look good and fit very well on the wall, but they’re just expensive relays and would not solve the issue. When switched off they also turned off the bulbs. It would need a custom firmware, but not all smart switches allow us to change their firmware.Then I watched this video from The Hook Up talking about the Shelly 1. That’s what I needed.I bought some units and flashed them with ESPHome.SAMPLE CONFIGESPHome config for the living room switch:esphome:  name: shelly01  platform: ESP8266  board: esp01_1m  on_boot:    priority: -10    then:      - switch.turn_on: relaywifi:  ssid: !secret wifi_ssid  password: !secret wifi_passwordlogger:api:ota:binary_sensor:  - platform: gpio    pin: GPIO5    name: "Living Room Light Switch"switch:  - platform: gpio    name: "Living Room Light Relay"    pin: GPIO4    id: relayThe living room lights:light:  - platform: group    - name: "Living Room Ceiling Lights"      entities:        - light.living_room_ceiling1        - light.living_room_ceiling2I use Node-RED, so I’m a bit rusty in YAML. The automation would look something like this:automation:- alias: 'Living Room Lights Turn ON'  trigger:    platform: state    entity_id: binary_sensor.living_room_light_switch  condition:    condition: state    entity_id: light.living_room_ceiling_lights    state: 'off'  action:    service: light.turn_on    data:      entity_id: light.living_room_ceiling_lights      kelvin: 7000      brightness_pct: 100- alias: 'Living Room Lights Turn OFF'  trigger:    platform: state    entity_id: binary_sensor.living_room_light_switch  condition:    condition: state    entity_id: light.living_room_ceiling_lights    state: 'on'  action:    service: light.turn_off    data:      entity_id: light.living_room_ceiling_lightsTHE PROS  Really fast response - It’s like turning on and off an ordinary light bulb. Most people would not even notice that they are connected devices.  Smart bulbs always on - You have the smart bulbs (and their features) available all the time.  Prevents smart bulbs resets - Some smart bulbs are reset to factory defaults if turned on and off a 5 or 6 times in a row. You can flick the light switch as much as you want and that won’t happen, only the LEDs will be switched on and off. Frenck would love this part! 😄.THE CONS  Additional cost - Not really a con since they are not expensive. But needs to be taken into account.  Relay always on - This worries me a bit because I think this can shorten its life span.  Connectivity - Home Assistant needs to be accessible by the switches all the time.I have been using this solution for a few months and am very happy with it, especially since I have had no issues so far."
    } ,
  
    {
      "title"    : "Making Home Assistant&#39;s Presence Detection not so Binary (Node-RED version)",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/making-home-assistants-presence-detection-not-so-binary-node-red-version/",
      "date"     : "2018-12-25 05:30:00 -0500",
      "content"  : "— EDIT (Dec 25, 2018) —If you use node-red-contrib-home-assistant-websocket (default in Node-RED Community Hass.io Add-on), the sequence can be even smaller.Since its version 0.3.0:  Call Service and Fire Event nodes are now able to render mustache templates in the data property.This means that we can remove almost all of the template nodes used in the original sequence and move their content to the call service nodes right after them.Note: The JSON code of this sequence is here.— ORIGINAL POST —My special thanks to Phil Hawthorne for letting me use the title of his post here.In early 2018 I found Phil’s excellent blog post “Making Home Assistant’s Presence Detection not so Binary” and began using the setup suggested by him almost immediately.A few months later I started migrating my automations to Node-RED and posted the initial results in the comments section of Phil’s post.This works great, but the caveat is that you have to have one sequence for each device tracked.I started looking for a way to have only one sequence for all the tracked devices, after some time (with some hits and many, many misses) this is what I came out with.Note: The JSON code of this sequence is here.Now all I needed was to set up an input_select with the same name of a device_tracker in Home Assistant.In known_devices.yaml:homer:  hide_if_away: false  icon:  mac: 00:00:00:00:00:00  name: Homer  picture: /local/homer.png  track: trueIn configuration.yaml:input_select:  homer:    options:      - Home      - Just Arrived      - Just Left      - Away      - Extended AwayAnd in Node-RED an events: state node that should be connected to the change node in the beginning of the sequence.This must be repeated for each person being tracked, just replacing the names where needed.For example:marge:  hide_if_away: false  icon:  mac: 11:11:11:11:11:11  name: Marge  picture: /local/marge.png  track: trueinput_select:  marge:    options:      - Home      - Just Arrived      - Just Left      - Away      - Extended AwayThe final result should be something like this:JSON codeNode-RED JSON code for the sequence in the beginning of this post.[    {        "id": "d40df6c2.d47028",        "type": "switch",        "z": "8563a7bd.384e78",        "name": "Home?",        "property": "status",        "propertyType": "msg",        "rules": [            {                "t": "eq",                "v": "home",                "vt": "str"            },            {                "t": "eq",                "v": "not_home",                "vt": "str"            }        ],        "checkall": "false",        "repair": false,        "outputs": 2,        "x": 400,        "y": 100,        "wires": [            [                "c06ebef7.051a7"            ],            [                "1c5fa89c.e32cc7"            ]        ]    },    {        "id": "1c5fa89c.e32cc7",        "type": "template",        "z": "8563a7bd.384e78",        "name": "Just Left",        "field": "payload",        "fieldType": "msg",        "format": "handlebars",        "syntax": "mustache",        "template": "{ \"domain\": \"input_select\",\n  \"service\": \"select_option\",\n  \"data\": {\n    \"entity_id\": \"input_select.{{topic}}\",\n    \"option\": \"Just Left\"\n   }\n}",        "output": "str",        "x": 540,        "y": 140,        "wires": [            [                "c6209771.e44568"            ]        ]    },    {        "id": "c06ebef7.051a7",        "type": "template",        "z": "8563a7bd.384e78",        "name": "",        "field": "payload.entity_id",        "fieldType": "msg",        "format": "handlebars",        "syntax": "mustache",        "template": "input_select.{{topic}}",        "output": "str",        "x": 540,        "y": 60,        "wires": [            [                "ee350274.26dfb"            ]        ]    },    {        "id": "ee350274.26dfb",        "type": "api-current-state",        "z": "8563a7bd.384e78",        "name": "Status?",        "halt_if": "",        "override_topic": false,        "override_payload": true,        "entity_id": "",        "x": 680,        "y": 60,        "wires": [            [                "b9e404f5.ac9ee8",                "72adcd60.18ef94"            ]        ]    },    {        "id": "b9e404f5.ac9ee8",        "type": "switch",        "z": "8563a7bd.384e78",        "name": "Just Left?",        "property": "payload",        "propertyType": "msg",        "rules": [            {                "t": "neq",                "v": "Just Left",                "vt": "str"            },            {                "t": "else"            }        ],        "checkall": "true",        "repair": false,        "outputs": 2,        "x": 820,        "y": 40,        "wires": [            [                "bff37b9c.a09628"            ],            [                "49cf7023.7e66"            ]        ]    },    {        "id": "72adcd60.18ef94",        "type": "change",        "z": "8563a7bd.384e78",        "name": "RESET",        "rules": [            {                "t": "set",                "p": "reset",                "pt": "msg",                "to": "true",                "tot": "bool"            }        ],        "action": "",        "property": "",        "from": "",        "to": "",        "reg": false,        "x": 810,        "y": 80,        "wires": [            [                "7becd2f9.0dd21c",                "f4835fc3.0baf2"            ]        ]    },    {        "id": "85a514ff.5e7dd8",        "type": "change",        "z": "8563a7bd.384e78",        "name": "RESET",        "rules": [            {                "t": "set",                "p": "reset",                "pt": "msg",                "to": "true",                "tot": "bool"            }        ],        "action": "",        "property": "",        "from": "",        "to": "",        "reg": false,        "x": 1170,        "y": 140,        "wires": [            [                "6586b018.1ce49",                "f4835fc3.0baf2"            ]        ]    },    {        "id": "c6209771.e44568",        "type": "api-call-service",        "z": "8563a7bd.384e78",        "name": "HA",        "service_domain": "",        "service": "",        "data": "",        "mergecontext": "",        "x": 670,        "y": 140,        "wires": [            [                "7becd2f9.0dd21c",                "85a514ff.5e7dd8"            ]        ]    },    {        "id": "1213e3a7.af404c",        "type": "api-call-service",        "z": "8563a7bd.384e78",        "name": "HA",        "service_domain": "",        "service": "",        "data": "",        "mergecontext": "",        "x": 1110,        "y": 20,        "wires": [            [                "6586b018.1ce49"            ]        ]    },    {        "id": "9f3a6cdc.4bc01",        "type": "api-call-service",        "z": "8563a7bd.384e78",        "name": "HA",        "service_domain": "",        "service": "",        "data": "",        "mergecontext": "",        "x": 1050,        "y": 120,        "wires": [            [                "f4835fc3.0baf2"            ]        ]    },    {        "id": "a6ac7a43.a32818",        "type": "api-call-service",        "z": "8563a7bd.384e78",        "name": "HA",        "service_domain": "",        "service": "",        "data": "",        "mergecontext": "",        "x": 1530,        "y": 80,        "wires": [            []        ]    },    {        "id": "7becd2f9.0dd21c",        "type": "trigger",        "z": "8563a7bd.384e78",        "op1": "",        "op2": "",        "op1type": "nul",        "op2type": "pay",        "duration": "10",        "extend": false,        "units": "min",        "reset": "",        "bytopic": "topic",        "name": "10 Min",        "x": 810,        "y": 120,        "wires": [            [                "f78cad65.d2879"            ]        ]    },    {        "id": "f4835fc3.0baf2",        "type": "trigger",        "z": "8563a7bd.384e78",        "op1": "",        "op2": "",        "op1type": "nul",        "op2type": "pay",        "duration": "24",        "extend": false,        "units": "hr",        "reset": "",        "bytopic": "topic",        "name": "24 Hrs",        "x": 1230,        "y": 80,        "wires": [            [                "a73294ba.2c45b8"            ]        ]    },    {        "id": "6586b018.1ce49",        "type": "trigger",        "z": "8563a7bd.384e78",        "op1": "",        "op2": "",        "op1type": "nul",        "op2type": "pay",        "duration": "10",        "extend": false,        "units": "min",        "reset": "",        "bytopic": "topic",        "name": "10 Min",        "x": 1230,        "y": 20,        "wires": [            [                "49cf7023.7e66"            ]        ]    },    {        "id": "bff37b9c.a09628",        "type": "template",        "z": "8563a7bd.384e78",        "name": "Just Arrived",        "field": "payload",        "fieldType": "msg",        "format": "handlebars",        "syntax": "mustache",        "template": "{ \"domain\": \"input_select\",\n  \"service\": \"select_option\",\n  \"data\": {\n    \"entity_id\": \"input_select.{{topic}}\",\n    \"option\": \"Just Arrived\"\n   }\n}",        "output": "str",        "x": 970,        "y": 20,        "wires": [            [                "1213e3a7.af404c"            ]        ]    },    {        "id": "49cf7023.7e66",        "type": "template",        "z": "8563a7bd.384e78",        "name": "Home",        "field": "payload",        "fieldType": "msg",        "format": "handlebars",        "syntax": "mustache",        "template": "{ \"domain\": \"input_select\",\n  \"service\": \"select_option\",\n  \"data\": {\n    \"entity_id\": \"input_select.{{topic}}\",\n    \"option\": \"Home\"\n   }\n}",        "output": "str",        "x": 1350,        "y": 40,        "wires": [            [                "2e01cc90.59e594"            ]        ]    },    {        "id": "a73294ba.2c45b8",        "type": "template",        "z": "8563a7bd.384e78",        "name": "Extended Away",        "field": "payload",        "fieldType": "msg",        "format": "handlebars",        "syntax": "mustache",        "template": "{ \"domain\": \"input_select\",\n  \"service\": \"select_option\",\n  \"data\": {\n    \"entity_id\": \"input_select.{{topic}}\",\n    \"option\": \"Extended Away\"\n   }\n}",        "output": "str",        "x": 1380,        "y": 80,        "wires": [            [                "a6ac7a43.a32818"            ]        ]    },    {        "id": "f78cad65.d2879",        "type": "template",        "z": "8563a7bd.384e78",        "name": "Away",        "field": "payload",        "fieldType": "msg",        "format": "handlebars",        "syntax": "mustache",        "template": "{ \"domain\": \"input_select\",\n  \"service\": \"select_option\",\n  \"data\": {\n    \"entity_id\": \"input_select.{{topic}}\",\n    \"option\": \"Away\"\n   }\n}",        "output": "str",        "x": 930,        "y": 120,        "wires": [            [                "9f3a6cdc.4bc01"            ]        ]    },    {        "id": "25670fd1.5262b",        "type": "change",        "z": "8563a7bd.384e78",        "name": "Change",        "rules": [            {                "t": "move",                "p": "payload",                "pt": "msg",                "to": "status",                "tot": "msg"            },            {                "t": "change",                "p": "topic",                "pt": "msg",                "from": "device_tracker.",                "fromt": "str",                "to": "",                "tot": "str"            }        ],        "action": "",        "property": "",        "from": "",        "to": "",        "reg": false,        "x": 260,        "y": 100,        "wires": [            [                "d40df6c2.d47028"            ]        ]    },    {        "id": "2e01cc90.59e594",        "type": "api-call-service",        "z": "8563a7bd.384e78",        "name": "HA",        "service_domain": "",        "service": "",        "data": "",        "mergecontext": "",        "x": 1470,        "y": 40,        "wires": [            []        ]    }]IMPORTANT: Don’t forget to review the Server configuration in the Home Assistant nodes if you are going to use this sequence.— EDIT (Dec 25, 2018) —JSON code v2Node-RED JSON code for the sequence in the beginning of this post.[    {        "id": "b3cfb392.48ca3",        "type": "switch",        "z": "e7991d63.81fb",        "name": "Home?",        "property": "status",        "propertyType": "msg",        "rules": [            {                "t": "eq",                "v": "home",                "vt": "str"            },            {                "t": "eq",                "v": "not_home",                "vt": "str"            }        ],        "checkall": "false",        "repair": false,        "outputs": 2,        "x": 500,        "y": 380,        "wires": [            [                "9b6b31e1.6730b"            ],            [                "a61252d8.10fcb"            ]        ]    },    {        "id": "9b6b31e1.6730b",        "type": "template",        "z": "e7991d63.81fb",        "name": "",        "field": "payload.entity_id",        "fieldType": "msg",        "format": "handlebars",        "syntax": "mustache",        "template": "input_select.{{topic}}",        "output": "str",        "x": 640,        "y": 340,        "wires": [            [                "dff67339.913bc"            ]        ]    },    {        "id": "dff67339.913bc",        "type": "api-current-state",        "z": "e7991d63.81fb",        "name": "Status?",        "server": "2591f105.083aee",        "halt_if": "",        "halt_if_type": "str",        "halt_if_compare": "is",        "override_topic": false,        "override_payload": true,        "override_data": true,        "entity_id": "",        "state_type": "str",        "outputs": 1,        "x": 780,        "y": 340,        "wires": [            [                "75535264.27122c",                "c8c0f0c5.0248b"            ]        ]    },    {        "id": "75535264.27122c",        "type": "switch",        "z": "e7991d63.81fb",        "name": "Just Left?",        "property": "payload",        "propertyType": "msg",        "rules": [            {                "t": "neq",                "v": "Just Left",                "vt": "str"            },            {                "t": "else"            }        ],        "checkall": "true",        "repair": false,        "outputs": 2,        "x": 920,        "y": 320,        "wires": [            [                "8ec9325c.161b8"            ],            [                "5e1e544e.f92d7c"            ]        ]    },    {        "id": "c8c0f0c5.0248b",        "type": "change",        "z": "e7991d63.81fb",        "name": "RESET",        "rules": [            {                "t": "set",                "p": "reset",                "pt": "msg",                "to": "true",                "tot": "bool"            }        ],        "action": "",        "property": "",        "from": "",        "to": "",        "reg": false,        "x": 910,        "y": 360,        "wires": [            [                "3168e10c.9094fe",                "8c42cf94.02f97"            ]        ]    },    {        "id": "5094372.30750c8",        "type": "change",        "z": "e7991d63.81fb",        "name": "RESET",        "rules": [            {                "t": "set",                "p": "reset",                "pt": "msg",                "to": "true",                "tot": "bool"            }        ],        "action": "",        "property": "",        "from": "",        "to": "",        "reg": false,        "x": 1050,        "y": 420,        "wires": [            [                "f5770290.c56e7",                "8c42cf94.02f97"            ]        ]    },    {        "id": "a61252d8.10fcb",        "type": "api-call-service",        "z": "e7991d63.81fb",        "name": "Just Left",        "server": "2591f105.083aee",        "service_domain": "input_select",        "service": "select_option",        "data": "{\"entity_id\":\"input_select.{{topic}}\",\"option\":\"Just Left\"}",        "render_data": true,        "mergecontext": "",        "output_location": "payload",        "output_location_type": "msg",        "x": 640,        "y": 420,        "wires": [            [                "3168e10c.9094fe",                "5094372.30750c8"            ]        ]    },    {        "id": "8ec9325c.161b8",        "type": "api-call-service",        "z": "e7991d63.81fb",        "name": "Just Arrived",        "server": "2591f105.083aee",        "service_domain": "input_select",        "service": "select_option",        "data": "{\"entity_id\":\"input_select.{{topic}}\",\"option\":\"Just Arrived\"}",        "render_data": true,        "mergecontext": "",        "output_location": "payload",        "output_location_type": "msg",        "x": 1070,        "y": 300,        "wires": [            [                "f5770290.c56e7"            ]        ]    },    {        "id": "c065d7d1.bce048",        "type": "api-call-service",        "z": "e7991d63.81fb",        "name": "Away",        "server": "2591f105.083aee",        "service_domain": "input_select",        "service": "select_option",        "data": "{\"entity_id\":\"input_select.{{topic}}\",\"option\":\"Away\"}",        "render_data": true,        "mergecontext": "",        "output_location": "payload",        "output_location_type": "msg",        "x": 1050,        "y": 380,        "wires": [            [                "8c42cf94.02f97"            ]        ]    },    {        "id": "74ed9275.0db39c",        "type": "api-call-service",        "z": "e7991d63.81fb",        "name": "Extended Away",        "server": "2591f105.083aee",        "service_domain": "input_select",        "service": "select_option",        "data": "{\"entity_id\":\"input_select.{{topic}}\",\"option\":\"Extended Away\"}",        "render_data": true,        "mergecontext": "",        "output_location": "payload",        "output_location_type": "msg",        "x": 1360,        "y": 360,        "wires": [            []        ]    },    {        "id": "3168e10c.9094fe",        "type": "trigger",        "z": "e7991d63.81fb",        "op1": "",        "op2": "{\"payload\":{\"data\":{\"option\":\"\"}}}",        "op1type": "nul",        "op2type": "json",        "duration": "10",        "extend": false,        "units": "min",        "reset": "",        "bytopic": "topic",        "name": "10 Min",        "x": 910,        "y": 400,        "wires": [            [                "c065d7d1.bce048"            ]        ]    },    {        "id": "8c42cf94.02f97",        "type": "trigger",        "z": "e7991d63.81fb",        "op1": "",        "op2": "{\"payload\":{\"data\":{\"option\":\"\"}}}",        "op1type": "nul",        "op2type": "json",        "duration": "24",        "extend": false,        "units": "hr",        "reset": "",        "bytopic": "topic",        "name": "24 Hrs",        "x": 1210,        "y": 360,        "wires": [            [                "74ed9275.0db39c"            ]        ]    },    {        "id": "f5770290.c56e7",        "type": "trigger",        "z": "e7991d63.81fb",        "op1": "",        "op2": "{\"payload\":{\"data\":{\"option\":\"\"}}}",        "op1type": "nul",        "op2type": "json",        "duration": "10",        "extend": false,        "units": "min",        "reset": "",        "bytopic": "topic",        "name": "10 Min",        "x": 1210,        "y": 300,        "wires": [            [                "5e1e544e.f92d7c"            ]        ]    },    {        "id": "5e1e544e.f92d7c",        "type": "api-call-service",        "z": "e7991d63.81fb",        "name": "Home",        "server": "2591f105.083aee",        "service_domain": "input_select",        "service": "select_option",        "data": "{\"entity_id\":\"input_select.{{topic}}\",\"option\":\"Home\"}",        "render_data": true,        "mergecontext": "",        "output_location": "payload",        "output_location_type": "msg",        "x": 1330,        "y": 320,        "wires": [            []        ]    },    {        "id": "9dbba4b1.4a1868",        "type": "change",        "z": "e7991d63.81fb",        "name": "Change",        "rules": [            {                "t": "move",                "p": "payload",                "pt": "msg",                "to": "status",                "tot": "msg"            },            {                "t": "change",                "p": "topic",                "pt": "msg",                "from": "device_tracker.",                "fromt": "str",                "to": "",                "tot": "str"            }        ],        "action": "",        "property": "",        "from": "",        "to": "",        "reg": false,        "x": 360,        "y": 380,        "wires": [            [                "b3cfb392.48ca3"            ]        ]    },    {        "id": "2591f105.083aee",        "type": "server",        "z": "",        "name": "Home Assistant",        "legacy": false,        "hassio": true,        "rejectUnauthorizedCerts": true,        "ha_boolean": "y|yes|true|on|home|open"    }]IMPORTANT: Don’t forget to review the Server configuration in the Home Assistant nodes if you are going to use this sequence."
    } ,
  
    {
      "title"    : "[Lovelace] Accessing Home Assistant states page with just one click",
      "category" : "",
      "tags"     : "HomeAssistant, Lovelace",
      "url"      : "/lovelace-accessing-home-assistant-states-page-with-just-one-click/",
      "date"     : "2018-12-24 05:30:00 -0500",
      "content"  : "— EDIT (Dec 24, 2018) —An alternative way to achieve this is to place a weblink in a Lovelace entities card.- type: entities  show_header_toggle: false  entities:    - type: weblink      url: /states      name: "States"      icon: mdi:home-assistant— EDIT (Nov 15, 2018) —WARNING: As of Home Assistant version 0.84 the method below no longer works.— ORIGINAL POST —Sometimes you need to access the Home Assistant states page (old UI) and when you have Lovelace as the default UI you have to type it in the address bar. Here is an easier way to access it.Add the following code to your configuration.yaml file and restart Home Assistant.IMPORTANT: Make sure you have the correct value in url:.panel_iframe:  states:    title: "States"    icon: mdi:home-assistant    url: http://&lt;your_home_assistant_url&gt;/statesNow you can access the states page with just one click."
    } ,
  
    {
      "title"    : "How to install Hass.io on Ubuntu Server 18.04",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/how-to-install-hass.io-on-ubuntu-server-18.04/",
      "date"     : "2018-11-17 05:30:00 -0500",
      "content"  : "— EDIT (Nov 17, 2018) —The best method to install Hass.io on a Generic Ubuntu/Debian machine is described at https://gist.github.com/frenck/32b4f74919ca6b95b30c66f85976ec58My thanks to Frenck for the awesome script.* DO NOT * follow the instructions below. I’m keeping them here just as a historical reference.— ORIGINAL POST —Until last month, I was running Hass.io on an Ubuntu Server 16.04. Now I decided to upgrade the host O.S. to the newest version. This is a step by step guide on how I did it.STEP 1: Perform a BACKUPI like to download the entire /usr/share/hassio directory. This is where the configuration files for Home Assistant (in /usr/share/hassio/homeassistant) and its add-ons (in /usr/share/hassio/addons) are located in Hass.io.Write down the address of each add-on repository that you have added to your installation, the name of all installed add-on and their configuration, they will be needed later.— EDIT (Oct 24, 2018) —Like John Mesberg well remembered in the comments section, you can use the Hass.io snapshot feature for the backup and restore process.STEP 2: Install host O.S.You can follow this guide explaining how to install Ubuntu Server 18.04.STEP 3: Make sure the host system is up to date.sudo apt-get updatesudo apt-get -y upgradeSome would say this is an optional step, but I think it’s a good practice.STEP 4: Install needed packages.— EDIT (Sep 12, 2018) —On the latest Ubuntu server, jq and docker.io are in the universe repository. Enable it with.sudo add-apt-repository universe &amp;&amp; sudo apt-get updateInstall the packages.sudo apt-get install docker.io avahi-daemon jqThese are the only packages required to install Hass.io on an Ubuntu Server.(OPTIONAL): If you want to make sure docker was correctly installed and it’s running you can run one of the commands below.sudo systemctl status dockersudo docker psSTEP 5: Install Hass.ioTo install Hass.io in this environment you need to run the following command as described in this page.curl -sL https://raw.githubusercontent.com/home-assistant/hassio-build/master/install/hassio_install | sudo bash -sThen you can access http://server_ip_address:8123 in your web browser.You should see this message. Just wait.After a few minutes the page should be refreshed automatically and you should see the page below.In some cases, after some time, you may receive an error message saying the page is inaccessible, a manual page refresh should resolve that.STEP 6: Reinstall, reconfigure and start add-ons.Click Hass.io in the left pane, and then click ADD-ON STORE.Add the repositories you wrote down in STEP 1, install the add-ons you had in your previous install, reconfigure and start them.STEP 7: Restore Home Assistant configuration files from backup.If you followed the guide in STEP 2 you should have access to the server via SSH.Connect to it using your preferred SCP client, remove all the files in /usr/share/hassio/homeassistant and upload the Home Assistant configuration files from your backup to that directory.STEP 8: Restart Home Assistant or reboot the host system.Then you can restart Home Assistant or reboot your system (only if you really want to).That’s it! Now you have Hass.io running on Ubuntu Server 18.04.— EDIT (May 18, 2018) —If you prefer to run Home Assistant in Python Virtual Environment BurnsHA has a great video explaining how to do it"
    } ,
  
    {
      "title"    : "How to run a Node-RED flow on Home Assistant start",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/make-a-node-red-flow-run-on-home-assistant-start/",
      "date"     : "2018-07-09 08:00:00 -0400",
      "content"  : "— EDIT (Jul 09, 2018) —I’ve just found a simpler and better way to do that. All we need is a status node and a switch node.In this case the status node checks for status changes in a selected node. I selected one of my home assistant “events: state” nodes. The switch node checks if the status node is sending the string “connected” in msg.status.text.Following is the JSON code of the sequence. Do not forget to change the status node configuration according to your environment.[    {        "id": "45fc4a1a.70c274",        "type": "status",        "z": "152abd73.69dcbb",        "name": "",        "scope": [            "95360a40.8cb83"        ],        "x": 100,        "y": 660,        "wires": [            [                "65572a91.71965c"            ]        ]    },    {        "id": "65572a91.71965c",        "type": "switch",        "z": "152abd73.69dcbb",        "name": "",        "property": "status.text",        "propertyType": "msg",        "rules": [            {                "t": "eq",                "v": "connected",                "vt": "str"            }        ],        "checkall": "true",        "repair": false,        "outputs": 1,        "x": 230,        "y": 660,        "wires": [            []        ]    }]— ORIGINAL POST —Here is an easy and simple way to run a Node-RED flow when Home Assistant starts.We start in Node-RED with an HTTP node.Then we edit it to listen to /hastart and choose a name.This will result in this node which you can insert in your flow and/or create a subflow to use it in multiple flows. Don’t forget to DEPLOY.Now, in Home Assistant, we create a shell_command and an automation that will run it.shell_command:  ha_start: 'curl http://localhost:1880/hastart'automation:  - alias: homeassistant_start    trigger:      - platform: homeassistant        event: start    action:      - service: shell_command.ha_startAnd that’s it. From now on, every time Home Assistant is started the node is triggered."
    } ,
  
    {
      "title"    : "How to hide offline Steam sensor entities in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/how-to-hide-offline-steam-sensor-entities-in-home-assistant/",
      "date"     : "2018-07-09 06:00:00 -0400",
      "content"  : "— EDIT (Jul 09, 2018) —Now with the new Lovelace UI it’s even easier to do it. We just need to use this entity-filter in ui-lovelace.yaml.- type: entity-filter  entities:    - sensor.steam_12345678901234567    - sensor.steam_98765432109847553    - sensor.steam_98409840789049048    - sensor.steam_90848949084989804  state_filter:    - 'online'    - 'busy'    - 'away'    - 'snooze'    - 'looking_to_trade'    - 'looking_to_play'  card:    type: glance    title: Steam  show_empty: false— ORIGINAL POST —I received the message below on Reddit and decided to find a way to do what was proposed.  Hi, this is great thanks so much for your contributions to the community. Blogs like yours are the reason I got in to HA in the first place.I’d be interested to see if you find a way to hide individual sensors from appearing groups. For example I have a group of steam sensors in a group called steam and would love to hide them individually when someone is not online without having to hide the whole group.A few hours later…sensor:  - platform: steam_online    api_key: !secret steam_api_key    accounts:      - 12345678901234567      - 98765432109847553      - 98409840789049048      - 90848949084989804— EDIT (Jun 18, 2018) —After a request here on the comments section and on Reddit I found out that, if the last or all entities of the sensor list in the automation template are offline, it would cause an error when the automation is run and therefore the frontend would not display the correct information in the group card.At first I did not know how to solve this and suggested to the requester that he tried to use CustomUI as an alternative.As the problem did not leave my head, I challenged myself to solve it. Then, after much trial and error, I came out with the following automation. It solves the problem of when the last entity is offline, and the group (group.steam) is not automatically created when all entities are offline.It also solves another problem I encountered during the tests. If only one entity is online (not the last), the group is now created correctly.I really did not like the if is_state_attr(steam.entity_id, 'icon', 'mdi:steam') part, but I did not find another way to select only the sensor.steam_* entities. If anyone knows a better way to do it please tell us how on the comments section below.Note: I’m keeping the old automation (commented out) below the current one for readers to understand the context.automation:- alias: 'Group Entities Online/Offline'  trigger:    - platform: homeassistant      event: start    - platform: time      minutes: '/1'      seconds: 0  action:    - service: group.set      data_template:        object_id: steam        entities: "{% set counter = 0 %}                {%- for steam in states.sensor if is_state_attr(steam.entity_id, 'icon', 'mdi:steam') and not(is_state(steam.entity_id, 'offline')) -%}                {% set counter = counter + 1 %}                {%- endfor -%}                {%- for steam in states.sensor if is_state_attr(steam.entity_id, 'icon', 'mdi:steam') and not(is_state(steam.entity_id, 'offline')) -%}                {%- if loop.first -%}{%- if counter == 1 -%}{{ steam.entity_id | lower }}{%- else -%}{{ steam.entity_id | lower }},{%- endif -%}{%- elif loop.last -%}{{ steam.entity_id | lower }}{%- else -%}{{ steam.entity_id | lower }},{%- endif -%}                {%- endfor -%}"#   - alias: 'Steam Group Entities 1'#     trigger:#       - platform: homeassistant#         event: start#     action:#       - service: group.set#         data_template:#           object_id: steam#           entities: "{% if not(is_state('sensor.steam_12345678901234567', 'offline')) %}sensor.steam_12345678901234567,{% endif %}#           {% if not(is_state('sensor.steam_98765432109847553', 'offline')) %}sensor.steam_98765432109847553,{% endif %}#           {% if not(is_state('sensor.steam_98409840789049048', 'offline')) %}sensor.steam_98409840789049048,{% endif %}#           {% if not(is_state('sensor.steam_90848949084989804', 'offline')) %}sensor.steam_90848949084989804{% endif %}"##   - alias: 'Steam Group Entities 2'#     trigger:#       - platform: time#         minutes: '/1'#         seconds: 0#     action:#       - service: group.set#         data_template:#           object_id: steam#           entities: "{% if not(is_state('sensor.steam_12345678901234567', 'offline')) %}sensor.steam_12345678901234567,{% endif %}#           {% if not(is_state('sensor.steam_98765432109847553', 'offline')) %}sensor.steam_98765432109847553,{% endif %}#           {% if not(is_state('sensor.steam_98409840789049048', 'offline')) %}sensor.steam_98409840789049048,{% endif %}#           {% if not(is_state('sensor.steam_90848949084989804', 'offline')) %}sensor.steam_90848949084989804{% endif %}"It works!Note: These SteamIDs do not existSome important points:  Pay attention to the commas after each sensor in the templates, the last doesn’t need one.  If you use views, don’t forget to add the group (in this case group.steam) to the desired view.  You don’t need to create the group, it will be created by the automations."
    } ,
  
    {
      "title"    : "Using Node-RED to capture Dash Button press",
      "category" : "",
      "tags"     : "HomeAssistant, NodeRED",
      "url"      : "/using-node-red-to-capture-dash-button-press/",
      "date"     : "2018-05-28 16:30:00 -0400",
      "content"  : "— EDIT (May 28, 2018) —With the release of Home Assistant version 0.70 it is now possible to install Dasher add-on again when running Hass.io on Ubuntu Server.— ORIGINAL POST —If, like me, you are frustrated by the inability to install certain add-ons in the latest versions of Hass.io running on a generic Linux Server, you should be looking for alternatives.One of the add-ons I was missing the most was Dasher because I use Dash Buttons to control some scenes in my house.As I do not have the patience to wait for the next versions of Hass.io the add-on (which may or may not solve the problem) I started searching and found Amazon Dash Button with Node-RED built-in nodes.This article explains everything textually so I decided to prepare something more visual to make it easier to understand it.Based on what’s written there I created the following subflow.[    {        "id": "27274ec0.477d32",        "type": "subflow",        "name": "Dash Button Press",        "info": "",        "in": [],        "out": [            {                "x": 460,                "y": 40,                "wires": [                    {                        "id": "4d2c0a32.de2794",                        "port": 0                    }                ]            },            {                "x": 460,                "y": 120,                "wires": [                    {                        "id": "4d2c0a32.de2794",                        "port": 1                    }                ]            }        ]    },    {        "id": "10f156e0.c68be9",        "type": "udp in",        "z": "27274ec0.477d32",        "name": "",        "iface": "",        "port": "67",        "ipv": "udp4",        "multicast": "false",        "group": "",        "datatype": "buffer",        "x": 70,        "y": 80,        "wires": [            [                "94bc49a4.4eb12"            ]        ]    },    {        "id": "94bc49a4.4eb12",        "type": "function",        "z": "27274ec0.477d32",        "name": "",        "func": "var mac = Buffer.alloc(6);\nmsg.payload.copy(mac, targetStart=0, sourceStart=28, sourceEnd=34);\nmsg.mac = mac.toString('hex');\nreturn msg;",        "outputs": 1,        "noerr": 0,        "x": 210,        "y": 80,        "wires": [            [                "4d2c0a32.de2794"            ]        ]    },    {        "id": "4d2c0a32.de2794",        "type": "switch",        "z": "27274ec0.477d32",        "name": "",        "property": "mac",        "propertyType": "msg",        "rules": [            {                "t": "eq",                "v": "78e103b8b5c8",                "vt": "str"            },            {                "t": "eq",                "v": "78e1031a7c50",                "vt": "str"            }        ],        "checkall": "false",        "repair": false,        "outputs": 2,        "x": 350,        "y": 80,        "wires": [            [],            []        ]    }]The switch node has two outputs, one for each one of the buttons I use. You’ll need to have as many outputs as the quantity of buttons you want to use.After that I just created the following flow connecting each output to its respective node and deploy the flows.If you’re curious, these are the switches I currently use.switch:  - platform: template    switches:      dash_bedroom:        value_template: "{{ is_state('light.bedroom_lights', 'on') }}"        turn_on:          service: script.turn_on          entity_id: script.good_morning        turn_off:          service: script.turn_on          entity_id: script.good_nightswitch:  - platform: template    switches:      living_room_lights:        value_template: "{{ is_state('light.living_room_lights', 'on') }}"        turn_on:          service: script.turn_on          entity_id: script.turn_on_1s        turn_off:          service: script.turn_on          entity_id: script.turn_off_1sThe best thing about this is that it seems to work faster than with the add-ons in Home Assistant."
    } ,
  
    {
      "title"    : "Track battery levels with Home Assistant and Custom UI",
      "category" : "",
      "tags"     : "HomeAssistant, CustomUI",
      "url"      : "/track-battery-levels-with-home-assistant-and-custom-ui/",
      "date"     : "2018-04-25 07:25:00 -0400",
      "content"  : "If you have sensors around the house, you should be concerned about the battery life of these sensors. Unless they are hard wired, of course. 🙂I was reading this example from the Home Assistant Cookbook and started to think “why not change the colors of the entities according to their battery level?”.It turns out that there is no way to color entities only with Home Assistant, for that we will have to use Custom UI. Its installation is quite simple, just follow one of the procedures described in documentation.I chose to install it manually and activated using the code below like described in the Activating section of the documentation.customizer:  custom_ui: localThen it was time to have fun with the YAML files.frontend:  themes:    alert_yellow:      primary-text-color: '#FFC000'      paper-item-icon-color: '#FFC000'    alert_red:      primary-text-color: '#FF0000'      paper-item-icon-color: '#FF0000'First I created two themes, alert_yellow and alert_red. Each of them define the colors for the text (name and state) and for the icon of a given entity.sensor:  - platform: template    sensors:      window_sensor_battery_level:        unit_of_measurement: '%'        value_template: '{{ states.input_number.window_sensor_battery.state|int }}'        icon_template: &gt;          {% set battery_level = states.sensor.window_sensor_battery_level.state|default(0)|int %}          {% set battery_round = (battery_level / 10) |int * 10 %}          {% if battery_round &gt;= 100 %}            mdi:battery          {% elif battery_round &gt; 0 %}            mdi:battery-{{ battery_round }}          {% else %}            mdi:battery-alert          {% endif %}      door_sensor_battery_level:        unit_of_measurement: '%'        value_template: '{{ states.input_number.door_sensor_battery.state|int }}'        icon_template: &gt;          {% set battery_level = states.sensor.door_sensor_battery_level.state|default(0)|int %}          {% set battery_round = (battery_level / 10) |int * 10 %}          {% if battery_round &gt;= 100 %}            mdi:battery          {% elif battery_round &gt; 0 %}            mdi:battery-{{ battery_round }}          {% else %}            mdi:battery-alert          {% endif %}Then created the sensors using the icon_template code found in the Cookbook to show the icon corresponding to each battery level.Note: I used two input_number to simulate the battery charge values of each sensor.customize:  sensor.window_sensor_battery_level:    custom_ui_state_card: state-card-custom-ui    friendly_name: "Window Sensor Battery Level"    templates:      theme: &gt;        if (state &lt; 20) {          return 'alert_red';        } else if (state &lt; 40) {          return 'alert_yellow';        }  sensor.door_sensor_battery_level:    custom_ui_state_card: state-card-custom-ui    friendly_name: "Door Sensor Battery Level"    templates:      theme: &gt;        if (state &lt; 20) {          return 'alert_red';        } else if (state &lt; 40) {          return 'alert_yellow';        }And finally I customized each sensor. If the battery charge is below 40% the sensor will be displayed in yellow and if it’s below 20% it will be displayed in red. If the values are 40% and above the sensor will be displayed using the colors defined in the theme used at that moment.Here are some samples of the output.Using this with a good notification system will help prevent a sensor from stop responding due to a discharged battery. Additionally, this will give you the possibility to choose the best time to change each battery and minimize the risk of discarding batteries that can still be used for some time.Note: Unfortunately this is not working with Safari 11.1 on MacOS High Sierra, I had to use Chrome."
    } ,
  
    {
      "title"    : "Dynamically hide/unhide an entire view in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/dynamically-hide-unhide-an-entire-view-update/",
      "date"     : "2018-04-19 07:25:00 -0400",
      "content"  : "Reddit user /u/lordjustice17 sent me a message (see below) on Reddit with a different use of Dynamically hide/unhide an entire view in Home AssistantHis code:- platform: template switches:   console:     friendly_name: Control Panel     value_template: "{{ is_state_attr('group.console', 'view', true) }}"     turn_on:       service: group.set       data:         object_id: console         view: true     turn_off:       service: group.set       data:         object_id: console         view: falseThis creates a switch that shows and hides a view called console. Clever!"
    } ,
  
    {
      "title"    : "Use an IP phone to &#39;say&#39; Home Assistant notifications",
      "category" : "",
      "tags"     : "HomeAssistant, FreePBX, Asterisk",
      "url"      : "/use-an-ip-phone-to-say-home-assistant-notifications/",
      "date"     : "2018-04-10 12:05:00 -0400",
      "content"  : "After I posted Blink lights when your IP phone rings, I kept thinking “Why not use my IP phone to ‘say’ notifications?”The way I did it was creating a Paging Group on my FreePBX server.Then on Home Assistant I have the following.shell_command:  mauricio_arrived: printf 'Channel: Local/724464@from-internal\nApplication: Flite\nData: "*. Welcome home, Mauricio."' &gt; `date +"%Y%m%d%H%M%S"`.call &amp;&amp; scp *.call root@192.168.10.10:/var/spool/asterisk/outgoing &amp;&amp; rm *.callautomation:  - alias: Mark Mauricio as just arrived    trigger:      - platform: state        entity_id: input_boolean.mauricio_home        from: 'off'        to: 'on'    action:      - service: shell_command.mauricio_arrivedIt will generate a file with the content below and upload it via SCP to the directory /var/spool/asterisk/outgoing on the FreePBX server. Asterisk uses files in this directory to initiate calls automatically, see this and this.Channel: Local/724464@from-internalApplication: FliteData: "*. Welcome home, Mauricio."When I get home, the automation is triggered and I’m greeted with a robotic voice. 🤖Note 1: You must set SSH to use keys for authentication, this will prevent the system from prompting for passwords every time shell command is executed.Note 2: I inserted ‘*.’ in the text because my phone (Sangoma S500) has a two second beep that is played “before” the audio, but it overlaps the beggining of the text being ‘said’. I tried removing the beep in the phone configuration, but then I had a two second silence with the same problem."
    } ,
  
    {
      "title"    : "How I used Caddy Proxy to publish this blog before migrating it to GitHub Pages",
      "category" : "",
      "tags"     : "HomeAssistant, DuckDNS, CaddyProxy, GitHub",
      "url"      : "/how-i-used-caddy-proxy-to-publish-this-blog-before-migrating-it-to-github-pages/",
      "date"     : "2018-04-04 06:40:00 -0400",
      "content"  : "Before migrating this blog to GitHub Pages I used this Caddy Proxy add-on on Hass.io to help me publish it.When using Caddy Proxy, there is no need to worry about certificates in the configuration.yaml file, it takes care of that automatically. Just let Home Assistant run on the default port (8123).As I don’t have a static IP on my internet connection I had to use the DuckDNS add-on to translate my dynamic IP address to a hostname.The first step was to create an account and a domain on the DuckDNS website.After that I copied the domain and token to the DuckDNS add-on Config, saved and started the add-on.DuckDNS add-on Config example:{  "lets_encrypt": {    "accept_terms": true,    "certfile": "fullchain.pem",    "keyfile": "privkey.pem"  },  "token": "sdfj-2131023-dslfjsd-12321",  "domains": ["my-domain.duckdns.org"]}Time to edit some DNS entries.I use Cloudflare to manage my domain. All I had to do was add a CNAME record pointing the root of my domain (@) to my DuckDNS address.I don’t like NAT Reflection/NAT Loopback/NAT Hairpinning, so I created an internal DNS zone to be able to access the blog in the LAN.I set up a forward on ports 80 and 443 to the IP address of the Caddy Proxy (in this case the same IP of my Home Assistant install) in my router.I also set up the Caddy Proxy add-on like below:{  "homeassistant": "hassio_hostname",  "vhosts": [    {      "vhost": "bonani.tech",      "port": "8081",      "remote": "10.10.10.10"    }  ],  "raw_config": [],  "email": "user@email.com"}Then I installed WordPress on port 8081 (because it was running on the same server).docker run --network=bridge -p 8081:80 -v /opt/wordpress/html:/var/www/html --name wordpress -d wordpressAnd VOILÀ!"
    } ,
  
    {
      "title"    : "Blink lights when your IP phone rings",
      "category" : "",
      "tags"     : "HomeAssistant, FreePBX, Asterisk",
      "url"      : "/blink-lights-when-your-ip-phone-rings/",
      "date"     : "2018-03-21 11:48:00 -0400",
      "content"  : "I have an IP phone on my desk connected to a FreePBX server. It is permanently set to silent mode. I keep it that way because 90% of the time I work with headphones. It’s also in a place where I can not easily see it.So how do I know when the phone is ringing? Easy. The lights flash in a different color from the ambient light, in this case Navy Blue.And how did I set this up? Also easy.On the FreePBX server, all incoming calls are redirected to a Ring Group containing two extensions:      My desk phone - extension 200        “Fake” extension - extension ****5678  The **5678 extension is created in the extensions_custom.conf file.[from-internal-custom]exten =&gt; ****5678,1,TrySystem(/home/asterisk/hass.sh &gt; /dev/null 2&gt;&amp;1 &amp;)same =&gt; n,CongestionIt calls the /home/asterisk/hass.sh shell script.#!/bin/bashcurl -X POST -H "x-ha-access: HASS_PASSWORD" -H "Content-Type: application/json" http://HASS_SERVER_ADDRESS:8123/api/services/script/incoming_callWhich triggers the incoming_call script in Home Assistant.# FreePBX Incoming Callincoming_call:  sequence:    - service: light.lifx_effect_pulse      data:        entity_id: light.lamp_lights        color_name: navy        brightness: 191        period: 1        cycles: 2    - delay:        seconds: 4    - service: light.lifx_effect_pulse      data:        entity_id: light.lamp_lights        color_name: navy        brightness: 191        period: 1        cycles: 2    - delay:        seconds: 4    - service: light.lifx_effect_pulse      data:        entity_id: light.lamp_lights        color_name: navy        brightness: 191        period: 1        cycles: 2When a call is received, the phone “rings” silently and the lights blink in navy blue three times separated by delays of 4 seconds."
    } ,
  
    {
      "title"    : "Managing groups visibility in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/managing-groups-visibility-in-home-assistant/",
      "date"     : "2018-03-18 15:54:00 -0400",
      "content"  : "After I wrote Dynamically hide / unhide an entire view in Home Assistant, many people asked me if it is possible to do the same with the cards (groups). It is, and it’s easy too.Here is an example code:group:  default_view:    view: yes    entities:      - group.test_group      - group.test_subgroup1      - group.test_subgroup2      - group.test_subgroup3  test_group:    name: "Test Group"    control: hidden    entities:      - input_boolean.test_input_boolean1      - input_boolean.test_input_boolean2      - input_boolean.test_input_boolean3  test_subgroup1:    name: "Test Subgroup 1"    entities:      - sensor.sensor_1  test_subgroup2:    name: "Test Subgroup 2"    entities:      - sensor.sensor_2  test_subgroup3:    name: "Test Subgroup 3"    entities:      - sensor.sensor_3input_boolean:  test_input_boolean1:    name: "Change Subgroup 1 visibility"    initial: on  test_input_boolean2:    name: "Change Subgroup 2 visibility"  test_input_boolean3:    name: "Change Subgroup 3 visibility"sensor:  - platform: random    name: Sensor 1  - platform: random    name: Sensor 2  - platform: random    name: Sensor 3customize:  group.test_subgroup2:    visible: false  group.test_subgroup3:    visible: falseautomation:  - alias: "group_view_default"    trigger:      - platform: homeassistant        event: start    action:      - service: group.set_visibility        entity_id: group.test_subgroup1        data:          visible: true      - service: group.set_visibility        entity_id: group.test_subgroup2        data:          visible: false      - service: group.set_visibility        entity_id: group.test_subgroup3        data:          visible: false  - alias: "subgroup1_visible"    trigger:      - platform: state        entity_id: input_boolean.test_input_boolean1        to: "on"    action:      - service: group.set_visibility        entity_id: group.test_subgroup1        data:          visible: true  - alias: "subgroup1_hidden"    trigger:      - platform: state        entity_id: input_boolean.test_input_boolean1        to: "off"    action:      - service: group.set_visibility        entity_id: group.test_subgroup1        data:          visible: false  - alias: "subgroup2_visible"    trigger:      - platform: state        entity_id: input_boolean.test_input_boolean2        to: "on"    action:      - service: group.set_visibility        entity_id: group.test_subgroup2        data:          visible: true  - alias: "subgroup2_hidden"    trigger:      - platform: state        entity_id: input_boolean.test_input_boolean2        to: "off"    action:      - service: group.set_visibility        entity_id: group.test_subgroup2        data:          visible: false  - alias: "subgroup3_visible"    trigger:      - platform: state        entity_id: input_boolean.test_input_boolean3        to: "on"    action:      - service: group.set_visibility        entity_id: group.test_subgroup3        data:          visible: true  - alias: "subgroup3_hidden"    trigger:      - platform: state        entity_id: input_boolean.test_input_boolean3        to: "off"    action:      - service: group.set_visibility        entity_id: group.test_subgroup3        data:          visible: falseAnd here is the result:"
    } ,
  
    {
      "title"    : "Dynamically hide/unhide an entire view in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/dynamically-hide-unhide-an-entire-view/",
      "date"     : "2018-03-15 17:03:00 -0400",
      "content"  : "Today I learned a way to hide and unhide a view in Home Assistant. It turns out it’s easier than I expected. All I had to do was use the group.set  service to change the attribute view.Here is an example code:group:  default_view:    name: "First"    view: yes    entities:      - script.show_view      - script.hide_view  second_view:    name: "Second"    view: yes    entities:      - script.show_view      - script.hide_viewscript:  show_view:    alias: "Show Second View"    sequence:      - service: group.set        data:          object_id: second_view          view: true  hide_view:    alias: "Hide Second View"    sequence:      - service: group.set        data:          object_id: second_view          view: falseThis generates two views (both visible). When I call the "Hide Second View" script, the "Second" view become hidden. And when I call the "Show Second View" script, the "Second" view become visible again.The only drawback is that if you only have two views and hide one of them, the blue bar will be resized, but the placement of the content below it will not be updated automatically."
    } ,
  
    {
      "title"    : "3 different ways to group light entities in Home Assistant",
      "category" : "",
      "tags"     : "HomeAssistant",
      "url"      : "/three-different-approaches-for-grouping-light-entities-in-home-assistant/",
      "date"     : "2018-03-12 17:00:00 -0400",
      "content"  : "I have three LIFX bulbs in a room, two in a ceiling fixture and one in a floor lamp. At first (months ago) I was controlling each bulb individually in the Home Assistant frontend, it was kinda annoying because any change in one of the ceiling fixture light bulbs should be manually replicated to the other one. Then I realized it would be much better to control them using groups.GroupThe original way to group entities was by using the Group component. Code and resulting card below.group:  room_lights:    name: "Group"    entities:      - group.room_ceiling_lights      - group.room_lamp_lights  room_ceiling_lights:    name: "Ceiling Lights"    entities:      - light.ceiling1      - light.ceiling2  room_lamp_lights:    name: "Lamp Lights"    entities:      - light.lamp1customize:  group.room_ceiling_lights:    friendly_name: "Ceiling Lights"    icon: mdi:ceiling-light  group.room_lamp_lights:    friendly_name: "Lamp Lights"    icon: mdi:lampWhen you click/tap on the name (or icon) of the group, Home Assistant displays this card where you can control some attributes.This was a great improvement in day-to-day use. Now I could turn on or off both ceiling lights at the same time, I could still change their attributes like brightness, color and color temperature at the same time. I was amazed.But (there is always a but) the color of the icons were not changed when the group was turned on or off (lights). This can be a bit frustrating if you use only the frontend to view and control those groups. That was the biggest drawback of this approach in my opinion.SwitchThen I found this discussion on the Home Assistant Community Forum which led me to this issue on GitHub where the use of the Switch component was suggested. I decided to try it using a Template Switch.  Code and resulting card below.switch:  - platform: template    switches:      room_ceiling_lights:        value_template: "{{ is_state('group.room_ceiling_lights', 'on') }}"        turn_on:          service: light.turn_on          entity_id: group.room_ceiling_lights        turn_off:          service: light.turn_off          entity_id: group.room_ceiling_lights  - platform: template    switches:      room_lamp_lights:        value_template: "{{ is_state('group.room_lamp_lights', 'on') }}"        turn_on:          service: light.turn_on          entity_id: group.room_lamp_lights        turn_off:          service: light.turn_off          entity_id: group.room_lamp_lightscustomize:  switch.room_ceiling_lights:    friendly_name: Ceiling    icon: mdi:ceiling-light  switch.room_lamp_lights:    friendly_name: Lamp    icon: mdi:lampgroup:  switch_group:    name: "Switch"    control: hidden    entities:      - switch.room_ceiling_lights      - switch.room_lamp_lightsNow the icons were changed every time the corresponding group was turned on or off. On the other hand we lose the possibility of changing attributes.Light GroupBeginning in version 0.65 we have a new platform called Light Group in the Light component. So I decided to test it. Code and resulting card below.light:  - platform: lifx  - platform: group    name: Ceiling Lights    entities:      - light.ceiling1      - light.ceiling2  - platform: group    name: Lamp Lights    entities:      - light.lamp1customize:  light.ceiling_lights:    friendly_name: "Ceiling Lights"    icon: mdi:ceiling-light  light.lamp_lights:    friendly_name: "Lamp Lights"    icon: mdi:lampgroup:  light_group:    name: "Light Group"    control: hidden    entities:      - light.ceiling_lights      - light.lamp_lightsAwesome. Now, besides the icons being changed with each change of state (on or off) of the groups, we have the ability to change attributes again.— EDIT (26 Apr, 2018) —Hasscasts has published a great video about Light GroupsConclusionParticularly I prefer using the new light group platform because it’s easier to setup, specially when compared to the template switch approach and yet has the advantage of controlling the groups attributes.Of course the choice depends on the scenario and use cases. How do you group your light entities? Let us know in the comments section below."
    } ,
  
    {
      "title"    : "Hello World",
      "category" : "",
      "tags"     : "",
      "url"      : "/hello-world/",
      "date"     : "2018-03-12 09:00:00 -0400",
      "content"  : "Hi, my name is Mauricio Bonani.I created this blog to publish small objective articles about things I’m learning and to improve my writing skills (from scratch)."
    } 
  
  ,
  
   {
     
   } ,
  
   {
     
        "title"    : "BonaniTech",
        "category" : "",
        "tags"     : "",
        "url"      : "/",
        "date"     : "",
        "content"  : "                              How to trigger a Node-RED flow from the Home Assistant UI — Jul 14, 2019I see a lot of people who use Node-RED as their automation engine looking for a way to trigger a flow from the Home Assistant UI. Here’s how I do...  Read more...                                    Smartening up a dumb light switch to control smart bulbs — Jun 1, 2019I see a lot of people having issues and/or doubts about the light switches when installing smart bulbs in their homes. This is how I do it in my house....  Read more...                                    Making Home Assistant's Presence Detection not so Binary (Node-RED version) — Dec 25, 2018— EDIT (Dec 25, 2018) — If you use node-red-contrib-home-assistant-websocket (default in Node-RED Community Hass.io Add-on), the sequence can be even smaller. Since its version 0.3.0: Call Service and Fire...  Read more...                                    [Lovelace] Accessing Home Assistant states page with just one click — Dec 24, 2018— EDIT (Dec 24, 2018) — An alternative way to achieve this is to place a weblink in a Lovelace entities card. - type: entities show_header_toggle: false entities: - type:...  Read more...                                    How to install Hass.io on Ubuntu Server 18.04 — Nov 17, 2018— EDIT (Nov 17, 2018) — The best method to install Hass.io on a Generic Ubuntu/Debian machine is described at https://gist.github.com/frenck/32b4f74919ca6b95b30c66f85976ec58 My thanks to Frenck for the awesome script. *...  Read more...                              &laquo; Prev              1                2                3                4            Next &raquo;    Subscribe via RSS and receive all new posts"
     
   } ,
  
   {
     
   } ,
  
   {
     
   } ,
  
   {
     
   } ,
  
   {
     
        "title"    : "Simple-Jekyll-Search",
        "category" : "",
        "tags"     : "",
        "url"      : "/assets/components/simple-jekyll-search/",
        "date"     : "",
        "content"  : "Simple-Jekyll-Search====================[![Build Status](https://travis-ci.org/christian-fei/Simple-Jekyll-Search.svg?branch=master)](https://travis-ci.org/christian-fei/Simple-Jekyll-Search)A JavaScript library to add search functionality to any Jekyll blog.---idea from this [blog post](https://alexpearce.me/2012/04/simple-jekyll-searching/#disqus_thread)---### Promotion: check out [Pomodoro.cc](https://pomodoro.cc/)# [Demo](http://christian-fei.github.io/Simple-Jekyll-Search/)# Getting started- Place the following code in a file called `search.json` in the **root** of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:```------[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}]```- configure the library ( [options](#options) )### Enabling full-text searchNote that the index generated in `search.json` does not include the posts' content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts' content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add```"content"  : "{{ post.content | strip_html | strip_newlines }}"```to `search.json` after the `"date"` line to which you must add a comma (`,`).# Install with bower```bower install simple-jekyll-search```# SetupYou need to place the following code within the layout where you want the search to appear.For example in  **_layouts/default.html**:``````# OptionsCustomize SimpleJekyllSearch by passing in your configuration options:```SimpleJekyllSearch({  searchInput: document.getElementById('search-input'),  resultsContainer: document.getElementById('results-container'),  json: '/search.json',})```The above initialization needs to occur after the inclusion of `jekyll-search.js`.### searchInput (Element) [required]The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.### resultsContainer (Element) [required]The container element in which the search results should be rendered in. Typically an ``.### json (String|JSON) [required]You can either pass in an URL to the `search.json` file, or the results in form of JSON directly, to save one round trip to get the data.### searchResultTemplateThe template of a single rendered search result.The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.E.g.The template```{title}```will render to the following```Welcome to Jekyll!```If the `search.json` contains this data```[    {      "title"    : "Welcome to Jekyll!",      "category" : "",      "tags"     : "",      "url"      : "/jekyll/update/2014/11/01/welcome-to-jekyll.html",      "date"     : "2014-11-01 21:07:22 +0100"    }]```### noResultsTextThe HTML that will be shown if the query didn't match anything.### limitYou can limit the number of posts rendered on the page.### fuzzyEnable fuzzy search to allow less restrictive matching.### excludePass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).## Enable full content search of posts and pages- Replace 'search.json' with the following code:```---layout: null---[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}",      "content"  : "{{ post.content | strip_html | strip_newlines }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}  ,  {% for page in site.pages %}   {     {% if page.title != nil %}        "title"    : "{{ page.title | escape }}",        "category" : "{{ page.category }}",        "tags"     : "{{ page.tags | join: ', ' }}",        "url"      : "{{ site.baseurl }}{{ page.url }}",        "date"     : "{{ page.date }}",        "content"  : "{{ page.content | strip_html | strip_newlines }}"     {% endif %}   } {% unless forloop.last %},{% endunless %}  {% endfor %}]```### If search isn't working due to invalid JSON- There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use `remove_chars` as a filter.For example: in search.json, replace```"content"  : "{{ page.content | strip_html | strip_newlines }}"```with```"content"  : "{{ page.content | strip_html | strip_newlines | remove_chars | escape }}"```##Browser supportBrowser support should be about IE6+ with this `addEventListener` [shim](https://gist.github.com/eirikbacker/2864711#file-addeventlistener-polyfill-js)# Dev setup- `npm install` the dependencies.- `gulp watch` during development- `npm test` or `npm run test-watch` to run the unit tests#License##MIT licensedPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
     
   } ,
  
   {
     
   } ,
  
   {
     
        "title"    : "BonaniTech",
        "category" : "",
        "tags"     : "",
        "url"      : "/page/2/",
        "date"     : "",
        "content"  : "          {% for post in paginator.posts %}                    {{ post.title }} — {{ post.date | date: "%b %-d, %Y" }}{{ post.content | strip_html | truncatewords: 30  }}  Read more...                {% endfor %}        {% include pagination.html %}  Subscribe via RSS and receive all new posts"
     
   } ,
  
   {
     
        "title"    : "BonaniTech",
        "category" : "",
        "tags"     : "",
        "url"      : "/page/3/",
        "date"     : "",
        "content"  : "          {% for post in paginator.posts %}                    {{ post.title }} — {{ post.date | date: "%b %-d, %Y" }}{{ post.content | strip_html | truncatewords: 30  }}  Read more...                {% endfor %}        {% include pagination.html %}  Subscribe via RSS and receive all new posts"
     
   } ,
  
   {
     
        "title"    : "BonaniTech",
        "category" : "",
        "tags"     : "",
        "url"      : "/page/4/",
        "date"     : "",
        "content"  : "          {% for post in paginator.posts %}                    {{ post.title }} — {{ post.date | date: "%b %-d, %Y" }}{{ post.content | strip_html | truncatewords: 30  }}  Read more...                {% endfor %}        {% include pagination.html %}  Subscribe via RSS and receive all new posts"
     
   } ,
  
   {
     
   } ,
  
   {
     
   } 
  
]

If search isn’t working due to invalid JSON

  • There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use remove_chars as a filter.

For example: in search.json, replace

"content"  : "Simple-Jekyll-Search====================[![Build Status](https://travis-ci.org/christian-fei/Simple-Jekyll-Search.svg?branch=master)](https://travis-ci.org/christian-fei/Simple-Jekyll-Search)A JavaScript library to add search functionality to any Jekyll blog.---idea from this [blog post](https://alexpearce.me/2012/04/simple-jekyll-searching/#disqus_thread)---### Promotion: check out [Pomodoro.cc](https://pomodoro.cc/)# [Demo](http://christian-fei.github.io/Simple-Jekyll-Search/)# Getting started- Place the following code in a file called `search.json` in the **root** of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:```------[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}]```- configure the library ( [options](#options) )### Enabling full-text searchNote that the index generated in `search.json` does not include the posts' content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts' content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add```"content"  : "{{ post.content | strip_html | strip_newlines }}"```to `search.json` after the `"date"` line to which you must add a comma (`,`).# Install with bower```bower install simple-jekyll-search```# SetupYou need to place the following code within the layout where you want the search to appear.For example in  **_layouts/default.html**:``````# OptionsCustomize SimpleJekyllSearch by passing in your configuration options:```SimpleJekyllSearch({  searchInput: document.getElementById('search-input'),  resultsContainer: document.getElementById('results-container'),  json: '/search.json',})```The above initialization needs to occur after the inclusion of `jekyll-search.js`.### searchInput (Element) [required]The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.### resultsContainer (Element) [required]The container element in which the search results should be rendered in. Typically an ``.### json (String|JSON) [required]You can either pass in an URL to the `search.json` file, or the results in form of JSON directly, to save one round trip to get the data.### searchResultTemplateThe template of a single rendered search result.The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.E.g.The template```{title}```will render to the following```Welcome to Jekyll!```If the `search.json` contains this data```[    {      "title"    : "Welcome to Jekyll!",      "category" : "",      "tags"     : "",      "url"      : "/jekyll/update/2014/11/01/welcome-to-jekyll.html",      "date"     : "2014-11-01 21:07:22 +0100"    }]```### noResultsTextThe HTML that will be shown if the query didn't match anything.### limitYou can limit the number of posts rendered on the page.### fuzzyEnable fuzzy search to allow less restrictive matching.### excludePass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).## Enable full content search of posts and pages- Replace 'search.json' with the following code:```---layout: null---[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}",      "content"  : "{{ post.content | strip_html | strip_newlines }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}  ,  {% for page in site.pages %}   {     {% if page.title != nil %}        "title"    : "{{ page.title | escape }}",        "category" : "{{ page.category }}",        "tags"     : "{{ page.tags | join: ', ' }}",        "url"      : "{{ site.baseurl }}{{ page.url }}",        "date"     : "{{ page.date }}",        "content"  : "{{ page.content | strip_html | strip_newlines }}"     {% endif %}   } {% unless forloop.last %},{% endunless %}  {% endfor %}]```### If search isn't working due to invalid JSON- There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use `remove_chars` as a filter.For example: in search.json, replace```"content"  : "{{ page.content | strip_html | strip_newlines }}"```with```"content"  : "{{ page.content | strip_html | strip_newlines | remove_chars | escape }}"```##Browser supportBrowser support should be about IE6+ with this `addEventListener` [shim](https://gist.github.com/eirikbacker/2864711#file-addeventlistener-polyfill-js)# Dev setup- `npm install` the dependencies.- `gulp watch` during development- `npm test` or `npm run test-watch` to run the unit tests#License##MIT licensedPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."

with

"content"  : "Simple-Jekyll-Search====================[![Build Status](https://travis-ci.org/christian-fei/Simple-Jekyll-Search.svg?branch=master)](https://travis-ci.org/christian-fei/Simple-Jekyll-Search)A JavaScript library to add search functionality to any Jekyll blog.---idea from this [blog post](https://alexpearce.me/2012/04/simple-jekyll-searching/#disqus_thread)---### Promotion: check out [Pomodoro.cc](https://pomodoro.cc/)# [Demo](http://christian-fei.github.io/Simple-Jekyll-Search/)# Getting started- Place the following code in a file called `search.json` in the **root** of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:```------[  {% for post in site.posts %}    {      &quot;title&quot;    : &quot;{{ post.title | escape }}&quot;,      &quot;category&quot; : &quot;{{ post.category }}&quot;,      &quot;tags&quot;     : &quot;{{ post.tags | join: &#39;, &#39; }}&quot;,      &quot;url&quot;      : &quot;{{ site.baseurl }}{{ post.url }}&quot;,      &quot;date&quot;     : &quot;{{ post.date }}&quot;    } {% unless forloop.last %},{% endunless %}  {% endfor %}]```- configure the library ( [options](#options) )### Enabling full-text searchNote that the index generated in `search.json` does not include the posts&#39; content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts&#39; content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add```&quot;content&quot;  : &quot;{{ post.content | strip_html | strip_newlines }}&quot;```to `search.json` after the `&quot;date&quot;` line to which you must add a comma (`,`).# Install with bower```bower install simple-jekyll-search```# SetupYou need to place the following code within the layout where you want the search to appear.For example in  **_layouts/default.html**:``````# OptionsCustomize SimpleJekyllSearch by passing in your configuration options:```SimpleJekyllSearch({  searchInput: document.getElementById(&#39;search-input&#39;),  resultsContainer: document.getElementById(&#39;results-container&#39;),  json: &#39;/search.json&#39;,})```The above initialization needs to occur after the inclusion of `jekyll-search.js`.### searchInput (Element) [required]The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.### resultsContainer (Element) [required]The container element in which the search results should be rendered in. Typically an ``.### json (String|JSON) [required]You can either pass in an URL to the `search.json` file, or the results in form of JSON directly, to save one round trip to get the data.### searchResultTemplateThe template of a single rendered search result.The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.E.g.The template```{title}```will render to the following```Welcome to Jekyll!```If the `search.json` contains this data```[    {      &quot;title&quot;    : &quot;Welcome to Jekyll!&quot;,      &quot;category&quot; : &quot;&quot;,      &quot;tags&quot;     : &quot;&quot;,      &quot;url&quot;      : &quot;/jekyll/update/2014/11/01/welcome-to-jekyll.html&quot;,      &quot;date&quot;     : &quot;2014-11-01 21:07:22 +0100&quot;    }]```### noResultsTextThe HTML that will be shown if the query didn&#39;t match anything.### limitYou can limit the number of posts rendered on the page.### fuzzyEnable fuzzy search to allow less restrictive matching.### excludePass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).## Enable full content search of posts and pages- Replace &#39;search.json&#39; with the following code:```---layout: null---[  {% for post in site.posts %}    {      &quot;title&quot;    : &quot;{{ post.title | escape }}&quot;,      &quot;category&quot; : &quot;{{ post.category }}&quot;,      &quot;tags&quot;     : &quot;{{ post.tags | join: &#39;, &#39; }}&quot;,      &quot;url&quot;      : &quot;{{ site.baseurl }}{{ post.url }}&quot;,      &quot;date&quot;     : &quot;{{ post.date }}&quot;,      &quot;content&quot;  : &quot;{{ post.content | strip_html | strip_newlines }}&quot;    } {% unless forloop.last %},{% endunless %}  {% endfor %}  ,  {% for page in site.pages %}   {     {% if page.title != nil %}        &quot;title&quot;    : &quot;{{ page.title | escape }}&quot;,        &quot;category&quot; : &quot;{{ page.category }}&quot;,        &quot;tags&quot;     : &quot;{{ page.tags | join: &#39;, &#39; }}&quot;,        &quot;url&quot;      : &quot;{{ site.baseurl }}{{ page.url }}&quot;,        &quot;date&quot;     : &quot;{{ page.date }}&quot;,        &quot;content&quot;  : &quot;{{ page.content | strip_html | strip_newlines }}&quot;     {% endif %}   } {% unless forloop.last %},{% endunless %}  {% endfor %}]```### If search isn&#39;t working due to invalid JSON- There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use `remove_chars` as a filter.For example: in search.json, replace```&quot;content&quot;  : &quot;{{ page.content | strip_html | strip_newlines }}&quot;```with```&quot;content&quot;  : &quot;{{ page.content | strip_html | strip_newlines | remove_chars | escape }}&quot;```##Browser supportBrowser support should be about IE6+ with this `addEventListener` [shim](https://gist.github.com/eirikbacker/2864711#file-addeventlistener-polyfill-js)# Dev setup- `npm install` the dependencies.- `gulp watch` during development- `npm test` or `npm run test-watch` to run the unit tests#License##MIT licensedPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#39;Software&#39;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED &#39;AS IS&#39;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."

##Browser support

Browser support should be about IE6+ with this addEventListener shim

Dev setup

  • npm install the dependencies.

  • gulp watch during development

  • npm test or npm run test-watch to run the unit tests

#License ##MIT licensed Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.