Full Code of smrtnt/Open-Home-Automation for AI

master a751db9c6a02 cached
81 files
276.5 KB
75.2k tokens
9 symbols
1 requests
Download .txt
Showing preview only (300K chars total). Download the full file or copy to clipboard to get everything.
Repository: smrtnt/Open-Home-Automation
Branch: master
Commit: a751db9c6a02
Files: 81
Total size: 276.5 KB

Directory structure:
gitextract_ko6a3yud/

├── .gitignore
├── LICENSE
├── README.md
├── ha_config_alarm_clock/
│   ├── README.md
│   └── configuration/
│       ├── automations.yaml
│       ├── configuration.yaml
│       ├── customize.yaml
│       ├── groups.yaml
│       ├── input_booleans.yaml
│       ├── input_numbers.yaml
│       ├── media_players.yaml
│       └── sensors.yaml
├── ha_config_zigate/
│   └── README.md
├── ha_mqtt_binary_sensor_ble_scanner/
│   ├── README.md
│   ├── example.config.h
│   └── ha_mqtt_binary_sensor_ble_scanner.ino
├── ha_mqtt_binary_sensor_door/
│   ├── README.md
│   └── ha_mqtt_binary_sensor_door.ino
├── ha_mqtt_binary_sensor_nfc_scanner/
│   ├── README.md
│   ├── example.config.h
│   └── ha_mqtt_binary_sensor_nfc_scanner.ino
├── ha_mqtt_binary_sensor_pir/
│   ├── README.md
│   └── ha_mqtt_binary_sensor_pir.ino
├── ha_mqtt_light/
│   ├── README.md
│   └── ha_mqtt_light.ino
├── ha_mqtt_light_arilux/
│   ├── LICENSE
│   ├── README.md
│   ├── example.config.h
│   └── ha_mqtt_light_arilux.ino
├── ha_mqtt_light_with_WiFiManager_mDNS_and_OTA/
│   ├── README.md
│   └── ha_mqtt_light_with_WiFiManager_mDNS_and_OTA.ino
├── ha_mqtt_light_with_brightness/
│   ├── README.md
│   └── ha_mqtt_light_with_brightness.ino
├── ha_mqtt_multisensor/
│   ├── Configuration/
│   │   ├── example.binary_sensor.yaml
│   │   ├── example.group.yaml
│   │   └── example.sensor.yaml
│   ├── LICENSE
│   ├── MultiSensor.cpp
│   ├── MultiSensor.h
│   ├── README.md
│   ├── example.config .h
│   └── ha_mqtt_multisensor.ino
├── ha_mqtt_rgb_light/
│   ├── README.md
│   └── ha_mqtt_rgb_light.ino
├── ha_mqtt_rgbw_light_with_discovery/
│   ├── README.md
│   ├── config.example.h
│   ├── ha_mqtt_rgbw_light_with_discovery.cpp
│   ├── ha_mqtt_rgbw_light_with_discovery.h
│   └── ha_mqtt_rgbw_light_with_discovery.ino
├── ha_mqtt_sensor_dht22/
│   ├── README.md
│   └── ha_mqtt_sensor_dht22.ino
├── ha_mqtt_sensor_photocell/
│   ├── README.md
│   └── ha_mqtt_sensor_photocell.ino
├── ha_mqtt_switch/
│   ├── README.md
│   └── ha_mqtt_switch.ino
└── openhome/
    ├── README.md
    ├── configuration/
    │   ├── home_assistant/
    │   │   ├── automation/
    │   │   │   ├── automation_1a.yaml
    │   │   │   ├── automation_1b.yaml
    │   │   │   ├── automation_1c.yaml
    │   │   │   ├── automation_1d.yaml
    │   │   │   ├── automation_2a.yaml
    │   │   │   ├── automation_2b.yaml
    │   │   │   ├── automation_3a.yaml
    │   │   │   ├── automation_3b.yaml
    │   │   │   ├── automation_3c.yaml
    │   │   │   └── automation_4a.yaml
    │   │   ├── automation.yaml
    │   │   ├── binary_sensors.yaml
    │   │   ├── configuration.yaml
    │   │   ├── customize.yaml
    │   │   ├── groups.yaml
    │   │   ├── input_numbers.yaml
    │   │   ├── known_devices.yaml
    │   │   ├── lights.yaml
    │   │   └── sensors.yaml
    │   └── mosquitto/
    │       ├── aclfile
    │       ├── mosquitto.conf
    │       └── pwfile
    └── sketches/
        ├── Bedroom/
        │   └── Bedroom.ino
        ├── Entrance/
        │   └── Entrance.ino
        └── Livingroom/
            └── Livingroom.ino

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================

.DS_Store
ha_mqtt_multisensor/config.h
ha_mqtt_multisensor/Configuration/binary_sensor.yaml
ha_mqtt_multisensor/Configuration/sensor.yaml
ha_mqtt_sensor_dht22/config.h
ha_mqtt_binary_sensor_ble_scanner/config.h
ha_mqtt_binary_sensor_nfc_scanner/config.h
ha_mqtt_light_arilux/config.h


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2016 

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.


================================================
FILE: README.md
================================================
# Open Home Automation

**THIS REPOSITORY IS NOT MAINTAINED ANYMORE, SEE [ESPHome](https://esphome.io/) AS AN ALTERNATIVE.**

## Introduction
Nowadays everything becomes connected to the Internet and gives us a glimpse of many new possibilities. Home automation is part of it and offers many advantages for their users.
This repository is dedicated to [Home Assistant](https://home-assistant.io), an open source project with an amazing community, ESP8266 and ESP32 modules, the MQTT protocol and much more [...].

### Home Assistant
> Home Assistant is a home automation platform running on Python 3. The goal of Home Assistant is to be able to track and control all devices at home and offer a platform for automating control [[Home-Assistant](https://github.com/home-assistant/home-assistant)].

### MQTT
> MQTT is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport. It is useful for connections with remote locations where a small code footprint is required and/or network bandwidth is at a premium [[mqtt.org](http://mqtt.org)].

![Home-Assistant](openhome/images/Features.png)

## Content
### Requirements
Most of the examples below are using the MQTT protocol and so require to integrate MQTT into Home Assistant by defining a MQTT broker. More information can be found at the [MQTT component's page](https://home-assistant.io/components/mqtt/).

#### Stock Home Assistant
configuration.yaml :

```yaml
mqtt:
  broker: 127.0.0.1
  port: 1883                      
  username: '[Redacted]'
  password: '[Redacted]'
  discovery: true                 # optional
  discovery_prefix: homeassistant # optional
```

#### Hass.io
configuration.yaml :

```yaml
mqtt:
  broker: core-mosquitto
  username: '[Redacted]'
  password: '[Redacted]'
  discovery: true                 # optional
  discovery_prefix: homeassistant # optional
```

More options to connect the broker are available and described [here](https://home-assistant.io/docs/mqtt/broker/#embedded-broker).

### Sketches for ESP8266/ESP32 modules
Lights, sensors, switches and more can be built on top of MQTT. This section contains a few examples based on MQTT and on a NodeMCU board (ESP8266/ESP32).

| Title / link                                  | Description                                                           |
|-----------------------------------------------|-----------------------------------------------------------------------|
| [Light](/ha_mqtt_light)                       | A simple example to control a **led**                                 |
| [Light](/ha_mqtt_light_with_brightness)       | A simple example to control a **led** and its brightness              |
| [Light](/ha_mqtt_rgb_light)                   | A simple example to control a **RGB led**                             |
| [Light](/ha_mqtt_light_with_WiFiManager_mDNS_and_OTA) | A simple example to control a **RGB led** (with **OTA** and **mDNS**)|
| [Light](https://github.com/mertenats/Arilux_AL-LC03)| An alternative firmware for the **Arilux AL-LC0X LED controller** |
| [Light](/ha_mqtt_light_arilux)| An alternative firmware for the **Arilux AL-LC0X LED controller** with multiple effects from the [WS2812FX](https://github.com/kitesurfer1404/WS2812FX) |
| [Light](/ha_mqtt_rgbw_light_with_discoveryw)    | A simple example to control a **RGBW led**, based on the **MQTT JSON Light** component (brightness, rgb, white, color temperature and effects) and including the **MQTT Discovery**                         |
| [Light](https://github.com/mertenats/AI-Thinker_RGBW_Bulb)    | An alternative firmware for **AI-Thinker RGBW bulbs**, based on the **MQTT JSON Light** component and including the **MQTT Discovery**  functionality                       |
| [Switch](/ha_mqtt_switch)                     | A simple example to control a **switch**    
| [Switch](https://github.com/mertenats/Itead_Sonoff/tree/master/Sonoff_Basic) | An alternative firmware for the **Sonoff Basic** switches                      |
| [Switch](https://github.com/mertenats/Itead_Sonoff/tree/master/Sonoff_TH) | An alternative firmware for the **Sonoff TH** switches, including the **MQTT Discovery**  functionality                 |
| [Sensor](/ha_mqtt_sensor_dht22)               | A simple example to measure the **temperature** and the **humidity** (DHT22 sensor)|
| [Sensor](/ha_mqtt_sensor_photocell)           | A simple example to measure the **brightness** (photocell)|
| [Binary Sensor](/ha_mqtt_binary_sensor_pir)   | A simple example to detect **motions** (PIR motion sensor)|
| [Binary Sensor](/ha_mqtt_binary_sensor_door)  | A simple example to monitor the **state** of a **window/door** |
| [Binary Sensor](/ha_mqtt_multisensor)         | A full example describing how to monitor your **environment** with different sensors (SHT3X, DHT22, TEMT6000, etc.)|
| [Binary Sensor](/ha_mqtt_binary_sensor_ble_scanner)| An example describing how to trigger an action when a specific **BLE device** is detected (ESP32)|
| [Binary Sensor](/ha_mqtt_binary_sensor_nfc_scanner)| An example describing how to trigger an action when a specific **NFC tag** is detected|

### Configuration examples for Home Assistant
This section contains a few configuration examples dedicated to Home Assistant.

| Title / link                                  | Description                                                           |
|-----------------------------------------------|-----------------------------------------------------------------------|
| [Alarm Clock](/ha_config_alarm_clock)         | An example describing how to create an **alarm clock** with the components provided by HA |
| [openhome](/openhome)                         | A school project based on Home Assistant and using only **open source** HW and SW |
| [Zigate & ZigBee devices](/ha_config_zigate)  | An example describing how to add and control **ZigBee** devices paired to a **Zigate** |

[![OpenHome with Home Assistant and MQTT](openhome/images/Youtube.png)](https://www.youtube.com/watch?v=Vh-vzFPCF2U "OpenHome with Home Assistant and MQTT")

## Miscelleneous

> 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.

*If you like the content of this repo, please add a star! Thank you!*


================================================
FILE: ha_config_alarm_clock/README.md
================================================
# Configuration - Alarm Clock - Home Assistant
A simple configuration example to create an alarm clock and trigger an automation, which turns on the light and switches on a stereo. This example was tested on [Home Assistant](https://home-assistant.io) 0.64.1 and uses the Philips [Hue](https://home-assistant.io/components/light.hue/) component and an [Onkyo](https://home-assistant.io/components/media_player.onkyo/) media player.

## Configuration
configuration.yaml :
```yaml
homeassistant:
  # Customization file
  customize: !include customize.yaml

hue:
  bridges:
   - host: 192.168.1.130

recorder:
  include:
    entities:
      - input_number.alarm_clock_hours
      - input_number.alarm_clock_minutes
      - input_boolean.alarm_clock_status

sensor: !include sensors.yaml
input_number: !include input_numbers.yaml
input_boolean: !include input_booleans.yaml
media_player: !include media_players.yaml
group: !include groups.yaml
automation: !include automations.yaml
```

customize.yaml :
```yaml
input_number.alarm_clock_hours:
  friendly_name: 'Heure'
  icon: mdi:timer
input_number.alarm_clock_minutes:
  friendly_name: 'Minute'
  icon: mdi:timer
input_boolean.alarm_clock_status:
  friendly_name: 'Etat'
  icon: mdi:calendar
sensor.alarm_clock_hours:
  hidden: true
sensor.alarm_clock_minutes:
  hidden: true
sensor.alarm_clock_time:
  friendly_name: 'Heure du réveil'
  icon: mdi:alarm
```

sensors.yaml :
```yaml
- platform: template
  sensors:
    alarm_clock_hours:
      value_template: "{{ states('input_number.alarm_clock_hours') | round(0) }}"
    alarm_clock_minutes:
      value_template: "{{ states('input_number.alarm_clock_minutes') | round(0) }}"
    alarm_clock_time:
      value_template: "{% if states.sensor.alarm_clock_hours.state | length == 1 -%}0{%- endif -%}{{ states.sensor.alarm_clock_hours.state }}:{% if states.sensor.alarm_clock_minutes.state | length == 1 -%}0{%- endif -%}{{ states.sensor.alarm_clock_minutes.state }}"
- platform: time_date
  display_options:
    - 'time'
```

input_numbers.yaml :
```yaml
alarm_clock_hours:
  min: 0
  max: 23
  step: 1
  mode: slider
alarm_clock_minutes:
  min: 0
  max: 55
  step: 5
  mode: slider
```

input_booleans.yaml :
```yaml
alarm_clock_status:
```

media_players.yaml :
```yaml
- platform: onkyo
  host: 192.168.1.133
  name: 'Stereo'
```

groups.yaml :
```yaml
default_view:
  view: yes
  entities:
    - group.alarm_clock

alarm_clock:
  name: 'Réveil'
  entities:
    - sensor.alarm_clock_time
    - input_number.alarm_clock_hours
    - input_number.alarm_clock_minutes
    - input_boolean.alarm_clock_status
```

automations.yaml :
```yaml
- alias: 'Turn on the alarm clock'
  trigger:
    platform: template
    value_template: '{{ states.sensor.time.state == states.sensor.alarm_clock_time.state }}'
  condition:
    condition: and
    conditions:
     - condition: state
       entity_id: input_boolean.alarm_clock_status
       state: 'on'
     - condition: time
       weekday:
         - mon
         - tue
         - wed
         - thu
         - fri
  action:
    - service: media_player.turn_on
      data:
        entity_id: media_player.stereo
    - service: media_player.volume_set
      data:
        entity_id: media_player.stereo
        volume_level: 0.07
    - service: media_player.select_source
      data:
        entity_id: media_player.stereo
        source: dab
    - service: light.turn_on
      data:
        entity_id: light.bedside
        brightness: 255
        transition: 900
    - delay:
        minutes: 15
    - service: light.turn_on
      data:
        entity_id: light.ceiling
        brightness: 255
    - service: light.turn_off
      data:
        entity_id: light.bedside
```

## Preview
![Alarm Clock](Alarm_clock.png)


================================================
FILE: ha_config_alarm_clock/configuration/automations.yaml
================================================
- alias: 'Turn on the alarm clock'
  trigger:
    platform: template
    value_template: '{{ states.sensor.time.state == states.sensor.alarm_clock_time.state }}'
  condition:
    condition: and
    conditions:
     - condition: state
       entity_id: input_boolean.alarm_clock_status
       state: 'on'
     - condition: time
       weekday:
         - mon
         - tue
         - wed
         - thu
         - fri
  action:
    - service: media_player.turn_on
      data:
        entity_id: media_player.stereo
    - service: media_player.volume_set
      data:
        entity_id: media_player.stereo
        volume_level: 0.07
    - service: media_player.select_source
      data:
        entity_id: media_player.stereo
        source: dab
    - service: light.turn_on
      data:
        entity_id: light.bedside
        brightness: 255
        transition: 900
    - delay:
        minutes: 15
    - service: light.turn_on
      data:
        entity_id: light.ceiling
        brightness: 255
    - service: light.turn_off
      data:
        entity_id: light.bedside


================================================
FILE: ha_config_alarm_clock/configuration/configuration.yaml
================================================
homeassistant:
  # Customization file
  customize: !include customize.yaml

hue:
  bridges:
   - host: 192.168.1.130

recorder:
  include:
    entities:
      - input_number.alarm_clock_hours
      - input_number.alarm_clock_minutes
      - input_boolean.alarm_clock_status

sensor: !include sensors.yaml
input_number: !include input_numbers.yaml
input_boolean: !include input_booleans.yaml
media_player: !include media_players.yaml
group: !include groups.yaml
automation: !include automations.yaml


================================================
FILE: ha_config_alarm_clock/configuration/customize.yaml
================================================
input_number.alarm_clock_hours:
  friendly_name: 'Heure'
  icon: mdi:timer
input_number.alarm_clock_minutes:
  friendly_name: 'Minute'
  icon: mdi:timer
input_boolean.alarm_clock_status:
  friendly_name: 'Status'
  icon: mdi:calendar
sensor.alarm_clock_hours:
  hidden: true
sensor.alarm_clock_minutes:
  hidden: true
sensor.alarm_clock_time:
  friendly_name: 'Réveil'
  icon: mdi:alarm


================================================
FILE: ha_config_alarm_clock/configuration/groups.yaml
================================================
default_view:
  view: yes
  entities:
    - group.alarm_clock

alarm_clock:
  name: 'Réveil'
  entities:
    - sensor.alarm_clock_time
    - input_number.alarm_clock_hours
    - input_number.alarm_clock_minutes
    - input_boolean.alarm_clock_status


================================================
FILE: ha_config_alarm_clock/configuration/input_booleans.yaml
================================================
alarm_clock_status:


================================================
FILE: ha_config_alarm_clock/configuration/input_numbers.yaml
================================================
alarm_clock_hours:
  min: 0
  max: 23
  step: 1
  mode: slider
alarm_clock_minutes:
  min: 0
  max: 55
  step: 5
  mode: slider


================================================
FILE: ha_config_alarm_clock/configuration/media_players.yaml
================================================
- platform: onkyo
  host: 192.168.1.133
  name: 'Stereo'


================================================
FILE: ha_config_alarm_clock/configuration/sensors.yaml
================================================
- platform: template
  sensors:
    alarm_clock_hours:
      value_template: "{{ states('input_number.alarm_clock_hours') | round(0) }}"
    alarm_clock_minutes:
      value_template: "{{ states('input_number.alarm_clock_minutes') | round(0) }}"
    alarm_clock_time:
      value_template: "{% if states.sensor.alarm_clock_hours.state | length == 1 -%}0{%- endif -%}{{ states.sensor.alarm_clock_hours.state }}:{% if states.sensor.alarm_clock_minutes.state | length == 1 -%}0{%- endif -%}{{ states.sensor.alarm_clock_minutes.state }}"
- platform: time_date
  display_options:
    - 'time'


================================================
FILE: ha_config_zigate/README.md
================================================
# Configuration - Zigate and ZigBee devices - Home Assistant
A simple configuration example to add and control ZigBee devices paired to a [Zigate](https://zigate.fr), an open source ZigBee concentrator.

## Requirements
### Software
Using the Zigate with Home Assistant requires to install `pyzigate` ([GitHub](https://github.com/elric91/ZiGate)) and copy the `homeassistant_zigate` component within the `custom_components` in Home Assistant's configuration folder ([GitHub](https://github.com/elric91/homeassistant_zigate)).

### Hardware
- 1x [Zigate](https://zigate.fr)
- 2x Philips white bulbs
- 1x Xiaomi Aqara switch
- 1x Xiaomi Aqara door sensor
- 1x Xiaomi Aqara temperature/humidity/pressure sensor

## Configuration
configuration.yaml :
```yaml
homeassistant:

...

switch: !include switches.yaml
light: !include lights.yaml
sensor: !include sensors.yaml
automation: !include automations.yaml
```

Configuration for the Xiaomi Aqara door sensor :

switches.yaml :
```yaml
- platform: zigate
  name: 'Door'
  address: XXXX01
  default_state: 'state'
  inverted: 'yes'
```

Configuration for the two white Philips Hue bulbs :

lights.yaml :
```yaml
- platform: zigate
  name: 'Bedside'
  address: XXXX0b
  light_type: 'white'
  default_state: 'event'
- platform: zigate
  name: 'Ceiling'
  address: XXXX0b
  light_type: 'white'
  default_state: 'event'
```
**Note** : Philips uses the `0b`cluster for their bulbs!

Configuration for the Xiaomi Aqara temperature/humidity/pressure sensor and switch :

sensors.yaml :
```yaml
- platform: zigate
  name: 'Ceiling Switch'
  address: XXXX01
  default_state: 'state'
- platform: zigate
  name: 'Temperature'
  address: XXXX01
  default_state: temperature
  default_unit: '°C'
- platform: zigate
  name: 'Humidity'
  address: XXXX01
  default_state: humidity
  default_unit: '%'
- platform: zigate
  name: 'Pressure'
  address: XXXX01
  default_state: pressure
  default_unit: 'mb'
```

Automation example to handle a single/double click on the Xiaomi Aqara switch :

automations.yaml :
```yaml
- alias: 'Ceiling Switch - Single Click'
  hide_entity: True
  trigger:
    entity_id: sensor.ceiling_switch
    platform: state
    to: 'off-release'
  action:
    service: light.toggle
    data:
      entity_id: light.ceiling
- alias: 'Ceiling Switch - Double Click'
  hide_entity: True
  trigger:
    entity_id: sensor.ceiling_switch
    platform: state
    to: 'multi_2'
  action:
    service: light.toggle
    data:
      entity_id: light.bedside
```

## References
- [Instructions pour les ampoules Philips Hue](http://zigate.fr/837-2/)
- [Configuration examples](https://github.com/elric91/homeassistant_zigate/wiki/Configuration-examples)


================================================
FILE: ha_mqtt_binary_sensor_ble_scanner/README.md
================================================
# MQTT Binary Sensor - Bluetooth LE Device Tracker - Home Assistant
A simple example describing how to track a Bluetooth Low Energy device with an ESP32, the MQTT protocol and Home Assistant. Please note that the targeted device can't have a changing BLE address (normally called random instead of public address).  

![ESP32](esp32.jpg)

## Configuration
To configure this sketch, you have to rename the header file `example.config.h` in `config.h`, provide the Bluetooth device(s) to track and add your Wi-Fi and MQTT credentials in the `SOFTWARE SECTION`.

### Bluetooth Device(s)
```
#define NB_OF_BLE_TRACKED_DEVICES 1
BLETrackedDevice BLETrackedDevices[NB_OF_BLE_TRACKED_DEVICES] = {
  {"11:22:33:44:55:66", false, 0, false, {0}}
};

// Location of the BLE scanner
#define LOCATION "bedroom"
```
or with multiple devices
```
#define NB_OF_BLE_TRACKED_DEVICES 3
BLETrackedDevice BLETrackedDevices[NB_OF_BLE_TRACKED_DEVICES] = {
  {"11:22:33:44:55:66", false, 0, false, {0}},
  {"11:22:33:44:55:66", false, 0, false, {0}},
  {"11:22:33:44:55:66", false, 0, false, {0}}
};

// Location of the BLE scanner
#define LOCATION "bedroom"
```

### Credentials
```
// Wi-Fi credentials
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""

// MQTT
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883
```

### Home Assistant
To add the occupancy sensor to Home Assistant, please edit and add this snippet into your configuration. Make sure to replace `<CHIP_ID`, `<LOCATION>` and `<BLE_ADDRESS>` with the values defined in `config.h`.

```yaml
# Example configuration.yaml entry
binary_sensor:
  - platform: mqtt
    name: 'Occupancy'
    state_topic: '<CHIP_ID>/sensor/<LOCATION>/<BLE_ADDRESS>/state'
    availability_topic: '<CHIP_ID/availability'
    device_class: occupancy
```

![Occupancy](occupancy.png)

## Licence
> 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.

*If you like the content of this repo, please add a star! Thank you!*


================================================
FILE: ha_mqtt_binary_sensor_ble_scanner/example.config.h
================================================
///////////////////////////////////////////////////////////////////////////
//  CONFIGURATION - SOFTWARE
///////////////////////////////////////////////////////////////////////////
#define NB_OF_BLE_TRACKED_DEVICES 1
BLETrackedDevice BLETrackedDevices[NB_OF_BLE_TRACKED_DEVICES] = {
  {"11:22:33:44:55:66", false, 0, false, {0}}
};

#define BLE_SCANNING_PERIOD   5
#define MAX_NON_ADV_PERIOD    10000

// Location of the BLE scanner
#define LOCATION "bedroom"

// Debug output
//#define DEBUG_SERIAL

// Wi-Fi credentials
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""

// Over-the-Air update
// Not implemented yet
//#define OTA
//#define OTA_HOSTNAME  ""    // hostname esp8266-[ChipID] by default
//#define OTA_PASSWORD  ""    // no password by default
//#define OTA_PORT      8266  // port 8266 by default

// MQTT
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883

#define MQTT_CONNECTION_TIMEOUT 5000 // [ms]

// MQTT availability: available/unavailable
#define MQTT_AVAILABILITY_TOPIC_TEMPLATE  "%s/availability" 
// MQTT binary sensor: <CHIP_ID>/sensor/<LOCATION>/<BLE_ADDRESS>
#define MQTT_SENSOR_TOPIC_TEMPLATE        "%s/sensor/%s/%s/state"

#define MQTT_PAYLOAD_ON   "ON"
#define MQTT_PAYLOAD_OFF  "OFF"

#define MQTT_PAYLOAD_AVAILABLE    "online"
#define MQTT_PAYLOAD_UNAVAILABLE  "offline"


================================================
FILE: ha_mqtt_binary_sensor_ble_scanner/ha_mqtt_binary_sensor_ble_scanner.ino
================================================
/*
  MQTT Binary Sensor - Bluetooth LE Device Tracker - Home Assistant
  
  Libraries:
    - PubSubClient: https://github.com/knolleary/pubsubclient
    - ESP32 BLE:    https://github.com/nkolban/ESP32_BLE_Arduino

  Sources:
    - https://github.com/nkolban/ESP32_BLE_Arduino/blob/master/examples/BLE_scan/BLE_scan.ino
    - https://www.youtube.com/watch?v=KNoFdKgvskU

  Samuel M. - v1.0 - 01.2018
  If you like this example, please add a star! Thank you!
  https://github.com/mertenats/open-home-automation
*/

typedef struct {
  String  address;
  bool    isDiscovered;
  long    lastDiscovery;
  bool    toNotify;
  char    mqttTopic[48];
} BLETrackedDevice;

#include "config.h"
#include <BLEDevice.h>
#include <WiFi.h>
#include <PubSubClient.h> // https://github.com/knolleary/pubsubclient

#if defined(DEBUG_SERIAL)
#define     DEBUG_PRINT(x)    Serial.print(x)
#define     DEBUG_PRINTLN(x)  Serial.println(x)
#else
#define     DEBUG_PRINT(x)
#define     DEBUG_PRINTLN(x)
#endif

BLEScan*      pBLEScan;
WiFiClient    wifiClient;
PubSubClient  mqttClient(wifiClient);

///////////////////////////////////////////////////////////////////////////
//   BLUETOOTH
///////////////////////////////////////////////////////////////////////////
class MyAdvertisedDeviceCallbacks:
  public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      for (uint8_t i = 0; i < NB_OF_BLE_TRACKED_DEVICES; i++) {
        if (strcmp(advertisedDevice.getAddress().toString().c_str(), BLETrackedDevices[i].address.c_str()) == 0) {
          if (!BLETrackedDevices[i].isDiscovered) {
            BLETrackedDevices[i].isDiscovered = true;
            BLETrackedDevices[i].lastDiscovery = millis();
            BLETrackedDevices[i].toNotify = true;

            DEBUG_PRINT(F("INFO: Tracked device newly discovered, Address: "));
            DEBUG_PRINT(advertisedDevice.getAddress().toString().c_str());
            DEBUG_PRINT(F(", RSSI: "));
            DEBUG_PRINTLN(advertisedDevice.getRSSI());
          } else {
            BLETrackedDevices[i].lastDiscovery = millis();
            DEBUG_PRINT(F("INFO: Tracked device discovered, Address: "));
            DEBUG_PRINT(advertisedDevice.getAddress().toString().c_str());
            DEBUG_PRINT(F(", RSSI: "));
            DEBUG_PRINTLN(advertisedDevice.getRSSI());
          }
        } else {
          DEBUG_PRINT(F("INFO: Device discovered, Address: "));
          DEBUG_PRINT(advertisedDevice.getAddress().toString().c_str());
          DEBUG_PRINT(F(", RSSI: "));
          DEBUG_PRINTLN(advertisedDevice.getRSSI());
        }
      }
    }
};

///////////////////////////////////////////////////////////////////////////
//   MQTT
///////////////////////////////////////////////////////////////////////////
volatile unsigned long lastMQTTConnection = 0;
char MQTT_CLIENT_ID[7] = {0};
char MQTT_AVAILABILITY_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_AVAILABILITY_TOPIC_TEMPLATE) - 2] = {0};
/*
  Function called to publish to a MQTT topic with the given payload
*/
void publishToMQTT(char* p_topic, char* p_payload) {
  if (mqttClient.publish(p_topic, p_payload, true)) {
    DEBUG_PRINT(F("INFO: MQTT message published successfully, topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(", payload: "));
    DEBUG_PRINTLN(p_payload);
  } else {
    DEBUG_PRINTLN(F("ERROR: MQTT message not published, either connection lost, or message too large. Topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(" , payload: "));
    DEBUG_PRINTLN(p_payload);
  }
}
/*
  Function called to connect/reconnect to the MQTT broker
*/
void connectToMQTT() {
  if (!mqttClient.connected()) {
    if (lastMQTTConnection < millis()) {
      if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD, MQTT_AVAILABILITY_TOPIC, 0, 1, MQTT_PAYLOAD_UNAVAILABLE)) {
        DEBUG_PRINTLN(F("INFO: The client is successfully connected to the MQTT broker"));
        publishToMQTT(MQTT_AVAILABILITY_TOPIC, MQTT_PAYLOAD_AVAILABLE);
      } else {
        DEBUG_PRINTLN(F("ERROR: The connection to the MQTT broker failed"));
        DEBUG_PRINT(F("INFO: MQTT username: "));
        DEBUG_PRINTLN(MQTT_USERNAME);
        DEBUG_PRINT(F("INFO: MQTT password: "));
        DEBUG_PRINTLN(MQTT_PASSWORD);
        DEBUG_PRINT(F("INFO: MQTT broker: "));
        DEBUG_PRINTLN(MQTT_SERVER);
      }
      lastMQTTConnection = millis() + MQTT_CONNECTION_TIMEOUT;
    }
  }
}

///////////////////////////////////////////////////////////////////////////
//   SETUP() & LOOP()
///////////////////////////////////////////////////////////////////////////
void setup() {
#if defined(DEBUG_SERIAL)
  Serial.begin(115200);
#endif

  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(false);

  mqttClient.setServer(MQTT_SERVER, MQTT_SERVER_PORT);

  sprintf(MQTT_CLIENT_ID, "%06X", ESP.getEfuseMac());
  sprintf(MQTT_AVAILABILITY_TOPIC, MQTT_AVAILABILITY_TOPIC_TEMPLATE, MQTT_CLIENT_ID);

  DEBUG_PRINT(F("INFO: MQTT availability topic: "));
  DEBUG_PRINTLN(MQTT_AVAILABILITY_TOPIC);

  char mqttTopic[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(LOCATION) + 12 - 4] = {0};
  for (uint8_t i = 0; i < NB_OF_BLE_TRACKED_DEVICES; i++) {
      char tmp_ble_address[13] = {0};
      String tmp_string_ble_address = BLETrackedDevices[i].address;
      tmp_string_ble_address.replace(":", "");
      tmp_string_ble_address.toCharArray(tmp_ble_address, sizeof(tmp_ble_address));
      sprintf(mqttTopic, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, LOCATION, tmp_ble_address);
      memcpy(BLETrackedDevices[i].mqttTopic, mqttTopic, sizeof(mqttTopic) + 1);
      DEBUG_PRINT(F("INFO: MQTT sensor topic: "));
      DEBUG_PRINTLN(BLETrackedDevices[i].mqttTopic);
  }

}

void loop() {
  pBLEScan->start(BLE_SCANNING_PERIOD);

  static boolean enableWifi = false;
  for (uint8_t i = 0; i < NB_OF_BLE_TRACKED_DEVICES; i++) {
    if (BLETrackedDevices[i].toNotify) {
      enableWifi = true;
    } else if (BLETrackedDevices[i].isDiscovered == true && BLETrackedDevices[i].lastDiscovery + MAX_NON_ADV_PERIOD < millis()) {
      BLETrackedDevices[i].isDiscovered = false;
      BLETrackedDevices[i].toNotify = true;
      enableWifi = true;
    }
  }

  if (enableWifi) {
    enableWifi = false;

    DEBUG_PRINT(F("INFO: WiFi connecting to: "));
    DEBUG_PRINTLN(WIFI_SSID);
    delay(10);
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    randomSeed(micros());
    
    while (WiFi.status() != WL_CONNECTED) {
      DEBUG_PRINT(F("."));
      delay(500);
    }
    DEBUG_PRINTLN();
    DEBUG_PRINTLN(WiFi.localIP());

    while (!mqttClient.connected()) {
      connectToMQTT();
    }

    for (uint8_t i = 0; i < NB_OF_BLE_TRACKED_DEVICES; i++) {
      if (BLETrackedDevices[i].toNotify) {
        if (BLETrackedDevices[i].isDiscovered) {
          publishToMQTT(BLETrackedDevices[i].mqttTopic, MQTT_PAYLOAD_ON);
        } else {
          publishToMQTT(BLETrackedDevices[i].mqttTopic, MQTT_PAYLOAD_OFF);
        }
        BLETrackedDevices[i].toNotify = false;
      }
    }

    mqttClient.disconnect();
    WiFi.mode(WIFI_OFF);
  }
}


================================================
FILE: ha_mqtt_binary_sensor_door/README.md
================================================
# MQTT Binary Sensor - Door - Home Assistant
A simple example to monitor the state of a door with a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
binary_sensor:
  platform: mqtt
  name: 'Door'
  state_topic: 'CBF777/binary_sensor/door/state'
  sensor_class: opening
```

## Schematic
Door sensor
- Door sensor leg 1 - D1
- Door sensor leg 2 - GND

Button
- Switch leg 1 - VCC
- Switch leg 2 - D2 - Resistor 10K Ohms - GND

## Wi-Fi and MQTT Configuration
![Steps](Steps.png)


================================================
FILE: ha_mqtt_binary_sensor_door/ha_mqtt_binary_sensor_door.ino
================================================
/* 
  MQTT Binary Sensor - Door sensor for Home-Assistant - NodeMCU (ESP8266)
  https://home-assistant.io/components/binary_sensor.mqtt/
  
  Libraries :
    - ESP8266 core for Arduino :  https://github.com/esp8266/Arduino
    - PubSubClient:               https://github.com/knolleary/pubsubclient
    - WiFiManager:                https://github.com/tzapu/WiFiManager
  
  Sources :
    - File > Examples > ES8266WiFi > WiFiClient
    - File > Examples > PubSubClient > mqtt_auth

  Steps:
    - Upload the firmware
    - Connect to the new Wi-Fi AP and memorize its name 
    - Choose your network and enter your MQTT username, password, broker 
      IP address and broker port
    - Update your configuration in Home Assistant

  Configuration (Home Assistant) : 
    binary_sensor:
      platform: mqtt
      name: 'Door'
      state_topic: 'CBF777/binary_sensor/door/state'
      sensor_class: opening

  Samuel M. - v1.0 - 11.2016
  If you like this example, please add a star! Thank you!
  https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>    // https://github.com/esp8266/Arduino
#include <WiFiManager.h>    // https://github.com/tzapu/WiFiManager
#include <PubSubClient.h>   // https://github.com/knolleary/pubsubclient/releases/tag/v2.6
#include <Ticker.h>
#include <EEPROM.h>
#include <ArduinoOTA.h>

#define               DEBUG                       // enable debugging
#define               STRUCT_CHAR_ARRAY_SIZE 24   // size of the arrays for MQTT username, password, etc.

// macros for debugging
#ifdef DEBUG
  #define             DEBUG_PRINT(x)    Serial.print(x)
  #define             DEBUG_PRINTLN(x)  Serial.println(x)
#else
  #define             DEBUG_PRINT(x)
  #define             DEBUG_PRINTLN(x)
#endif

// Pins used by the door sensor and the push button
const PROGMEM uint8_t DOOR_SENSOR_PIN = D1;
const PROGMEM uint8_t BUTTON_PIN      = D2;
const PROGMEM uint8_t BUILTINLED_PIN  = BUILTIN_LED;

// MQTT ID and topics
char                  MQTT_CLIENT_ID[7]                                           = {0};
char                  MQTT_BINARY_SENSOR_DOOR_STATE_TOPIC[STRUCT_CHAR_ARRAY_SIZE] = {0};
const char*           MQTT_ON_PAYLOAD                                             = "ON";
const char*           MQTT_OFF_PAYLOAD                                            = "OFF";

// MQTT settings
typedef struct {
  char                mqttUser[STRUCT_CHAR_ARRAY_SIZE]      = {0};
  char                mqttPassword[STRUCT_CHAR_ARRAY_SIZE]  = {0};
  char                mqttServer[STRUCT_CHAR_ARRAY_SIZE]    = {0};
  char                mqttPort[5]                           = {0};
} Settings;

uint8_t               doorState             = LOW;
uint8_t               currentDoorState      = doorState;
uint8_t               buttonState           = LOW;
uint8_t               currentButtonState    = buttonState;
long                  buttonStartPressed    = 0;
long                  buttonDurationPressed = 0;

enum CMD {
  CMD_NOT_DEFINED,
  CMD_DOOR_STATE_CHANGED,
  CMD_BUTTON_STATE_CHANGED,
};
volatile uint8_t cmd = CMD_NOT_DEFINED;

Settings      settings;
Ticker        ticker;
WiFiClient    wifiClient;
PubSubClient  mqttClient(wifiClient);


///////////////////////////////////////////////////////////////////////////
//   MQTT
///////////////////////////////////////////////////////////////////////////

/*
  Function called to publish the state of the door sensor
*/
void publishDoorState() {
  if (doorState == HIGH) {
    if (mqttClient.publish(MQTT_BINARY_SENSOR_DOOR_STATE_TOPIC, MQTT_ON_PAYLOAD, true)) {
      DEBUG_PRINT(F("INFO: MQTT message publish succeeded. Topic: "));
      DEBUG_PRINT(MQTT_BINARY_SENSOR_DOOR_STATE_TOPIC);
      DEBUG_PRINT(F(". Payload: "));
      DEBUG_PRINTLN(MQTT_ON_PAYLOAD);
    } else {
      DEBUG_PRINTLN(F("ERROR: MQTT message publish failed, either connection lost, or message too large"));
    }
  } else {
    if (mqttClient.publish(MQTT_BINARY_SENSOR_DOOR_STATE_TOPIC, MQTT_OFF_PAYLOAD, true)) {
      DEBUG_PRINT(F("INFO: MQTT message publish succeeded. Topic: "));
      DEBUG_PRINT(MQTT_BINARY_SENSOR_DOOR_STATE_TOPIC);
      DEBUG_PRINT(F(". Payload: "));
      DEBUG_PRINTLN(MQTT_OFF_PAYLOAD);
    } else {
      DEBUG_PRINTLN(F("ERROR: MQTT message publish failed, either connection lost, or message too large"));
    }
  }
}

/*
  Function called to connect/reconnect to the MQTT broker
*/
void reconnect() {
  uint8_t i = 0;
  while (!mqttClient.connected()) {
    if (mqttClient.connect(MQTT_CLIENT_ID, settings.mqttUser, settings.mqttPassword)) {
      DEBUG_PRINTLN(F("INFO: The client is successfully connected to the MQTT broker"));
    } else {
      DEBUG_PRINTLN(F("ERROR: The connection to the MQTT broker failed"));
      DEBUG_PRINT(F("Username: "));
      DEBUG_PRINTLN(settings.mqttUser);
      DEBUG_PRINT(F("Password: "));
      DEBUG_PRINTLN(settings.mqttPassword);
      DEBUG_PRINT(F("Broker: "));
      DEBUG_PRINTLN(settings.mqttServer);
      delay(1000);
      if (i == 3) {
        reset();
      }
      i++;
    }
  }
}

///////////////////////////////////////////////////////////////////////////
//   WiFiManager
///////////////////////////////////////////////////////////////////////////
/*
  Function called to toggle the state of the LED
*/
void tick() {
  digitalWrite(BUILTIN_LED, !digitalRead(BUILTIN_LED));
}

// flag for saving data
bool shouldSaveConfig = false;

// callback notifying us of the need to save config
void saveConfigCallback () {
  shouldSaveConfig = true;
}

void configModeCallback (WiFiManager *myWiFiManager) {
  ticker.attach(0.2, tick);
}

///////////////////////////////////////////////////////////////////////////
//   ISR
///////////////////////////////////////////////////////////////////////////
/*
  Function called when the door is opened/closed
*/
void doorStateChangedISR() {
  cmd = CMD_DOOR_STATE_CHANGED;
}

/*
  Function called when the button is pressed/released
*/
void buttonStateChangedISR() {
  cmd = CMD_BUTTON_STATE_CHANGED;
}

///////////////////////////////////////////////////////////////////////////
//   ESP
///////////////////////////////////////////////////////////////////////////
/*
  Function called to restart the switch
*/
void restart() {
  DEBUG_PRINTLN(F("INFO: Restart..."));
  ESP.reset();
  delay(1000);
}

/*
  Function called to reset the configuration of the switch
*/
void reset() {
  DEBUG_PRINTLN(F("INFO: Reset..."));
  WiFi.disconnect();
  delay(1000);
  ESP.reset();
  delay(1000);
}

///////////////////////////////////////////////////////////////////////////
//   Setup() and loop()
///////////////////////////////////////////////////////////////////////////
void setup() {
#ifdef DEBUG
  Serial.begin(115200);
#endif
  pinMode(DOOR_SENSOR_PIN,  INPUT_PULLUP);
  pinMode(BUTTON_PIN,       INPUT);
  pinMode(BUILTIN_LED,      OUTPUT);

  attachInterrupt(digitalPinToInterrupt(DOOR_SENSOR_PIN), doorStateChangedISR,    CHANGE);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN),      buttonStateChangedISR,  CHANGE);

  ticker.attach(0.6, tick);

  sprintf(MQTT_CLIENT_ID, "%06X", ESP.getChipId());
  DEBUG_PRINT(F("INFO: MQTT client ID/Hostname: "));
  DEBUG_PRINTLN(MQTT_CLIENT_ID);

  sprintf(MQTT_BINARY_SENSOR_DOOR_STATE_TOPIC, "%06X/binary_sensor/door/state", ESP.getChipId());
  DEBUG_PRINT(F("INFO: MQTT command topic: "));
  DEBUG_PRINTLN(MQTT_BINARY_SENSOR_DOOR_STATE_TOPIC);

  // load custom params
  EEPROM.begin(512);
  EEPROM.get(0, settings);
  EEPROM.end();

  WiFiManagerParameter custom_mqtt_user("mqtt-user", "MQTT User", settings.mqttUser, STRUCT_CHAR_ARRAY_SIZE);
  WiFiManagerParameter custom_mqtt_password("mqtt-password", "MQTT Password", settings.mqttPassword, STRUCT_CHAR_ARRAY_SIZE, "type = \"password\"");
  WiFiManagerParameter custom_mqtt_server("mqtt-server", "MQTT Broker IP", settings.mqttServer, STRUCT_CHAR_ARRAY_SIZE);
  WiFiManagerParameter custom_mqtt_port("mqtt-port", "MQTT Broker Port", settings.mqttPort, 5);

  WiFiManager wifiManager;

  wifiManager.addParameter(&custom_mqtt_user);
  wifiManager.addParameter(&custom_mqtt_password);
  wifiManager.addParameter(&custom_mqtt_server);
  wifiManager.addParameter(&custom_mqtt_port);

  wifiManager.setAPCallback(configModeCallback);
  wifiManager.setConfigPortalTimeout(180);
  // set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);

  if (!wifiManager.autoConnect(MQTT_CLIENT_ID)) {
    ESP.reset();
    delay(1000);
  }

  if (shouldSaveConfig) {
    strcpy(settings.mqttServer,   custom_mqtt_server.getValue());
    strcpy(settings.mqttPort,     custom_mqtt_port.getValue());
    strcpy(settings.mqttUser,     custom_mqtt_user.getValue());
    strcpy(settings.mqttPassword, custom_mqtt_password.getValue());

    EEPROM.begin(512);
    EEPROM.put(0, settings);
    EEPROM.end();
  }

  // configure MQTT
  mqttClient.setServer(settings.mqttServer, atoi(settings.mqttPort));

  // connect to the MQTT broker
  reconnect();

  ArduinoOTA.setHostname(MQTT_CLIENT_ID);
  ArduinoOTA.begin();

  ticker.detach();

  doorState = digitalRead(DOOR_SENSOR_PIN);
  buttonState = digitalRead(BUTTON_PIN);
  digitalWrite(BUILTIN_LED, HIGH);

  publishDoorState();
}

void loop() {
  ArduinoOTA.handle();

  yield();

  // keep the MQTT client connected to the broker
  if (!mqttClient.connected()) {
    reconnect();
  }
  mqttClient.loop();

  yield();

  switch (cmd) {
    case CMD_NOT_DEFINED:
      // do nothing ...
      break;
    case CMD_DOOR_STATE_CHANGED:
      currentDoorState = digitalRead(DOOR_SENSOR_PIN);
      if (doorState != currentDoorState) {
        if (currentDoorState == HIGH) {
          DEBUG_PRINTLN(F("INFO: Door opened"));
        } else {
          DEBUG_PRINTLN(F("INFO: Door closed"));
        }
        doorState = currentDoorState;
      }
      publishDoorState();
      cmd = CMD_NOT_DEFINED;
      break;
    case CMD_BUTTON_STATE_CHANGED:
      currentButtonState = digitalRead(BUTTON_PIN);
      if (buttonState != currentButtonState) {
        // tests if the button is released or pressed
        if (buttonState == HIGH && currentButtonState == LOW) {
          buttonDurationPressed = millis() - buttonStartPressed;
          if (buttonDurationPressed < 500) {
            // do nothing
            DEBUG_PRINTLN(F("INFO: Single press"));
          } else if (buttonDurationPressed < 3000) {
            DEBUG_PRINTLN(F("INFO: Restarting..."));
            restart();
          } else {
            DEBUG_PRINTLN(F("INFO: Reseting..."));
            reset();
          }
        } else if (buttonState == LOW && currentButtonState == HIGH) {
          buttonStartPressed = millis();
        }
        buttonState = currentButtonState;
      }
      cmd = CMD_NOT_DEFINED;
      break;
  }
}


================================================
FILE: ha_mqtt_binary_sensor_nfc_scanner/README.md
================================================
# MQTT Binary Sensor - NFC Tags Scanner - Home Assistant
A simple example describing how to scan NFC tags with an ESP8266 and send the results through MQTT to Home Assistant.

![NFC](nfc_scanner.jpg)

## Schematic
Requirements :
- PN532 NFC RFID (Elechouse module v3)
- ESP8266 (NodeMCU v1.0)

| NFC reader | NodeMCU / ESP8266  |
|------------|--------------------|
| SS (CS)    | D2 (GPIO4)         |
| SCK        | D5 (GPIO14)        |
| MISO       | D6 (GPIO12)        |
| MOSI       | D7 (GPIO13)        |
| VCC        | VCC (3V3)          |
| GND        | GND                |

## Configuration
To configure this sketch, you have to rename the header file `example.config.h` in `config.h`, provide the NFC tag's UID to track, its desired name (for example `MifareTag`) and add your Wi-Fi and MQTT credentials in the `SOFTWARE SECTION`.

### NFC Tags
```
#define NB_OF_NFC_TRACKED_TAGS 1
NFCTag NFCTags[NB_OF_NFC_TRACKED_TAGS] = {
  {{0x11, 0x22, 0x33, 0x44, 0x0, 0x0, 0x0}, "MifareTag", false, 0, false, {0}}
};

// Location of the NFC scanner
#define LOCATION "office"
```
or with multiple tags
```
#define NB_OF_NFC_TRACKED_TAGS 2
NFCTag NFCTags[NB_OF_NFC_TRACKED_TAGS] = {
  {{0x11, 0x22, 0x33, 0x44, 0x0, 0x0, 0x0}, "MifareTag", false, 0, false, {0}},
  {{0x12, 0x23, 0x34, 0x45, 0x0, 0x0, 0x0}, "MifareCard", false, 0, false, {0}},
};
```

### Credentials
```
// Wi-Fi credentials
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""

// MQTT
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883
```

### Home Assistant
To retrieve and use the results obtained with the NFC scanner into Home Assistant, please edit and add this snippet into your configuration. Make sure to replace `<CHIP_ID`, `<LOCATION>` and `<NFC_TAG_NAME>` with the values defined in `config.h`.

```yaml
# Example configuration.yaml entry
binary_sensor:
  - platform: mqtt
    name: 'NFC Tag'
    state_topic: '<CHIP_ID>/sensor/<LOCATION>/<NFC_TAG_NAME>/state'
    availability_topic: '<CHIP_ID>/availability'
  - platform: mqtt
    name: 'NFC Card'
    state_topic: '<CHIP_ID>/sensor/<LOCATION>/<NFC_TAG_NAME>/state'
    availability_topic: '<CHIP_ID>/availability'
```

![HA](ha.png)

## Licence
> 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.

*If you like the content of this repo, please add a star! Thank you!*


================================================
FILE: ha_mqtt_binary_sensor_nfc_scanner/example.config.h
================================================
///////////////////////////////////////////////////////////////////////////
//  CONFIGURATION - SOFTWARE
///////////////////////////////////////////////////////////////////////////
#define NB_OF_NFC_TRACKED_TAGS 2
NFCTag NFCTags[NB_OF_NFC_TRACKED_TAGS] = {
  {{0x11, 0x22, 0x33, 0x44, 0x0, 0x0, 0x0}, "MifareTag", false, 0, false, {0}},
  {{0x12, 0x23, 0x34, 0x45, 0x0, 0x0, 0x0}, "MifareCard", false, 0, false, {0}},
};

// send OFF payload after x ms
#define MAX_NON_DISCOVERED_PERIOD 1000

// Location of the BLE scanner
#define LOCATION "office"

// Debug output
#define DEBUG_SERIAL

// Wi-Fi credentials
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""

// MQTT
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883

#define MQTT_CONNECTION_TIMEOUT 5000 // [ms]

// MQTT availability: available/unavailable
#define MQTT_AVAILABILITY_TOPIC_TEMPLATE  "%s/availability"
// MQTT binary sensor: <CHIP_ID>/sensor/<LOCATION>/<BLE_ADDRESS>
#define MQTT_SENSOR_TOPIC_TEMPLATE        "%s/sensor/%s/%s/state"

#define MQTT_PAYLOAD_ON   "ON"
#define MQTT_PAYLOAD_OFF  "OFF"

#define MQTT_PAYLOAD_AVAILABLE    "online"
#define MQTT_PAYLOAD_UNAVAILABLE  "offline"


================================================
FILE: ha_mqtt_binary_sensor_nfc_scanner/ha_mqtt_binary_sensor_nfc_scanner.ino
================================================
/*
  MQTT Binary Sensor - NFC Tags Scanner - Home Assistant
  
  Libraries:
    - PubSubClient: https://github.com/knolleary/pubsubclient
    - PN532:        https://github.com/Seeed-Studio/PN532

  Sources:
    - Examples > PN532 > readMifare 

  Samuel M. - v1.0 - 02.2018
  If you like this example, please add a star! Thank you!
  https://github.com/mertenats/open-home-automation
*/

typedef struct {
  uint8_t uid[7];
  String  tagName;
  bool    isDiscovered;
  long    lastDiscovery;
  bool    toNotify;
  char    mqttTopic[48];
} NFCTag;

#include "config.h"
#include <ESP8266WiFi.h>
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"        // https://github.com/Seeed-Studio/PN532
#include <PubSubClient.h> // https://github.com/knolleary/pubsubclient

#if defined(DEBUG_SERIAL)
#define     DEBUG_PRINT(x)    Serial.print(x)
#define     DEBUG_PRINTLN(x)  Serial.println(x)
#else
#define     DEBUG_PRINT(x)
#define     DEBUG_PRINTLN(x)
#endif

WiFiClient    wifiClient;
PubSubClient  mqttClient(wifiClient);
PN532_SPI     pn532spi(SPI, 4);
PN532         nfc(pn532spi);

///////////////////////////////////////////////////////////////////////////
//   NFC
///////////////////////////////////////////////////////////////////////////
void readNFCTag() {
  uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID
  uint8_t uidLength;
  
  if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) {
    for (uint8_t i = 0; i < NB_OF_NFC_TRACKED_TAGS; i++) {
      if (strcmp((char *)uid, (char *)NFCTags[i].uid) == 0) {
        if (!NFCTags[i].isDiscovered) {
          NFCTags[i].isDiscovered = true;
          NFCTags[i].toNotify = true;
        }
        NFCTags[i].lastDiscovery = millis();
        break;
      }
    }
  }
  
  for (uint8_t i = 0; i < NB_OF_NFC_TRACKED_TAGS; i++) {
    if (NFCTags[i].isDiscovered == true && NFCTags[i].lastDiscovery + MAX_NON_DISCOVERED_PERIOD < millis()) {
      NFCTags[i].isDiscovered = false;
      NFCTags[i].toNotify = true;
    }
  }
}

///////////////////////////////////////////////////////////////////////////
//   WiFi
///////////////////////////////////////////////////////////////////////////
/*
   Function called to handle WiFi events
*/
void handleWiFiEvent(WiFiEvent_t event) {
  switch (event) {
    case WIFI_EVENT_STAMODE_GOT_IP:
      DEBUG_PRINTLN(F("INFO: Connection successful to the Wi-Fi AP"));
      DEBUG_PRINT(F("INFO: IP address: "));
      DEBUG_PRINTLN(WiFi.localIP());
      break;
    case WIFI_EVENT_STAMODE_DISCONNECTED:
      DEBUG_PRINTLN(F("ERROR: Connection lost from the Wi-Fi AP"));
      /*
         TODO: Doing something smarter than rebooting the device
      */
      delay(5000);
      ESP.restart();
      break;
    default:
      DEBUG_PRINT(F("INFO: WiFi event: "));
      DEBUG_PRINTLN(event);
      break;
  }
}

/*
   Function called to setup the connection to the WiFi AP
*/
void setupWiFi() {
  DEBUG_PRINT(F("INFO: WiFi connecting to: "));
  DEBUG_PRINTLN(WIFI_SSID);

  delay(10);

  WiFi.mode(WIFI_STA);
  WiFi.onEvent(handleWiFiEvent);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  randomSeed(micros());
}
///////////////////////////////////////////////////////////////////////////
//   MQTT
///////////////////////////////////////////////////////////////////////////
volatile unsigned long lastMQTTConnection = 0;
char MQTT_CLIENT_ID[7] = {0};
char MQTT_AVAILABILITY_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_AVAILABILITY_TOPIC_TEMPLATE) - 2] = {0};
/*
  Function called to publish to a MQTT topic with the given payload
*/
void publishToMQTT(char* p_topic, char* p_payload) {
  if (mqttClient.publish(p_topic, p_payload, true)) {
    DEBUG_PRINT(F("INFO: MQTT message published successfully, topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(", payload: "));
    DEBUG_PRINTLN(p_payload);
  } else {
    DEBUG_PRINTLN(F("ERROR: MQTT message not published, either connection lost, or message too large. Topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(" , payload: "));
    DEBUG_PRINTLN(p_payload);
  }
}
/*
  Function called to connect/reconnect to the MQTT broker
*/
void connectToMQTT() {
  if (!mqttClient.connected()) {
    if (lastMQTTConnection < millis()) {
      if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD, MQTT_AVAILABILITY_TOPIC, 0, 1, MQTT_PAYLOAD_UNAVAILABLE)) {
        DEBUG_PRINTLN(F("INFO: The client is successfully connected to the MQTT broker"));
        publishToMQTT(MQTT_AVAILABILITY_TOPIC, MQTT_PAYLOAD_AVAILABLE);
      } else {
        DEBUG_PRINTLN(F("ERROR: The connection to the MQTT broker failed"));
        DEBUG_PRINT(F("INFO: MQTT username: "));
        DEBUG_PRINTLN(MQTT_USERNAME);
        DEBUG_PRINT(F("INFO: MQTT password: "));
        DEBUG_PRINTLN(MQTT_PASSWORD);
        DEBUG_PRINT(F("INFO: MQTT broker: "));
        DEBUG_PRINTLN(MQTT_SERVER);
      }
      lastMQTTConnection = millis() + MQTT_CONNECTION_TIMEOUT;
    }
  }
}

///////////////////////////////////////////////////////////////////////////
//   SETUP() & LOOP()
///////////////////////////////////////////////////////////////////////////
void setup() {
#if defined(DEBUG_SERIAL)
  Serial.begin(115200);
#endif

  setupWiFi();

  nfc.begin();
 
  uint32_t nfcReaderFirmwareVersion = nfc.getFirmwareVersion();
  if (!nfcReaderFirmwareVersion) {
    DEBUG_PRINTLN(F("ERROR: Didn't find PN53x board"));
    while (1); // halt
  }
  
  // configure board to read RFID tags
  nfc.SAMConfig();
  
  sprintf(MQTT_CLIENT_ID, "%06X", ESP.getChipId());
  sprintf(MQTT_AVAILABILITY_TOPIC, MQTT_AVAILABILITY_TOPIC_TEMPLATE, MQTT_CLIENT_ID);

  DEBUG_PRINT(F("INFO: MQTT availability topic: "));
  DEBUG_PRINTLN(MQTT_AVAILABILITY_TOPIC);

  char mqttTopic[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(LOCATION) + 12 - 4] = {0};
  for (uint8_t i = 0; i < NB_OF_NFC_TRACKED_TAGS; i++) {
      char tmpTagName[sizeof(NFCTags[i].tagName)] = {0};
      NFCTags[i].tagName.toCharArray(tmpTagName, sizeof(tmpTagName));
      sprintf(mqttTopic, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, LOCATION, tmpTagName);
      memcpy(NFCTags[i].mqttTopic, mqttTopic, sizeof(mqttTopic) + 1);
      DEBUG_PRINT(F("INFO: MQTT sensor topic: "));
      DEBUG_PRINTLN(NFCTags[i].mqttTopic);
  }
  
  mqttClient.setServer(MQTT_SERVER, MQTT_SERVER_PORT);
  connectToMQTT();
}

void loop() {
  connectToMQTT();
  mqttClient.loop();

  yield();

  readNFCTag();

  yield();

  for (uint8_t i = 0; i < NB_OF_NFC_TRACKED_TAGS; i++) {
    if (NFCTags[i].toNotify) {
      if (NFCTags[i].isDiscovered) {
        publishToMQTT(NFCTags[i].mqttTopic, MQTT_PAYLOAD_ON);
      } else {
        publishToMQTT(NFCTags[i].mqttTopic, MQTT_PAYLOAD_OFF);
      }
      NFCTags[i].toNotify = false;
    }
  }
  
  yield();
}


================================================
FILE: ha_mqtt_binary_sensor_pir/README.md
================================================
# MQTT Binary Sensor - Motion - Home Assistant
A simple example to use a PIR motion sensor connected to a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
binary_sensor:
  platform: mqtt
  state_topic: 'office/motion/status'
  name: 'Motion'
  sensor_class: motion
```

## Schematic
- PIR leg 1 - VCC
- PIR leg 2 - D1/GPIO5
- PIR leg 3 - GND

![Schematic](Schematic.png)


================================================
FILE: ha_mqtt_binary_sensor_pir/ha_mqtt_binary_sensor_pir.ino
================================================
/*
   MQTT Binary Sensor - Motion (PIR) for Home-Assistant - NodeMCU (ESP8266)
   https://home-assistant.io/components/binary_sensor.mqtt/

   Libraries :
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient

   Sources :
    - File > Examples > ES8266WiFi > WiFiClient
    - File > Examples > PubSubClient > mqtt_auth
    - File > Examples > PubSubClient > mqtt_esp8266
    - https://learn.adafruit.com/pir-passive-infrared-proximity-motion-sensor/using-a-pir

   Schematic :
    - https://github.com/mertenats/open-home-automation/blob/master/ha_mqtt_binary_sensor_pir/Schematic.png
    - PIR leg 1 - VCC
    - PIR leg 2 - D1/GPIO5
    - PIR leg 3 - GND
    
   Configuration (HA) : 
     binary_sensor:
      platform: mqtt
      state_topic: 'office/motion/status'
      name: 'Motion'
      sensor_class: motion
      
   TODO :
    - Use the interrupts instead of constinously polling the sensor

   Samuel M. - v1.1 - 08.2016
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define MQTT_VERSION MQTT_VERSION_3_1_1

// Wifi: SSID and password
const PROGMEM char* WIFI_SSID = "[Redacted]";
const PROGMEM char* WIFI_PASSWORD = "[Redacted]";

// MQTT: ID, server IP, port, username and password
const PROGMEM char* MQTT_CLIENT_ID = "office_motion";
const PROGMEM char* MQTT_SERVER_IP = "[Redacted]";
const PROGMEM uint16_t MQTT_SERVER_PORT = 1883;
const PROGMEM char* MQTT_USER = "[Redacted]";
const PROGMEM char* MQTT_PASSWORD = "[Redacted]";

// MQTT: topic
const PROGMEM char* MQTT_MOTION_STATUS_TOPIC = "office/motion/status";

// default payload
const PROGMEM char* MOTION_ON = "ON";
const PROGMEM char* MOTION_OFF = "OFF";

// PIR : D1/GPIO5
const PROGMEM uint8_t PIR_PIN = 5;
uint8_t m_pir_state = LOW; // no motion detected
uint8_t m_pir_value = 0;

WiFiClient wifiClient;
PubSubClient client(wifiClient);

// function called to publish the state of the pir sensor
void publishPirSensorState() {
  if (m_pir_state) {
    client.publish(MQTT_MOTION_STATUS_TOPIC, MOTION_OFF, true);
  } else {
    client.publish(MQTT_MOTION_STATUS_TOPIC, MOTION_ON, true);
  }
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
      Serial.println("INFO: connected");
    } else {
      Serial.print("ERROR: failed, rc=");
      Serial.print(client.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  // init the serial
  Serial.begin(115200);

  // init the WiFi connection
  Serial.println();
  Serial.println();
  Serial.print("INFO: Connecting to ");
  Serial.println(WIFI_SSID);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("INFO: WiFi connected");
  Serial.println("INFO: IP address: ");
  Serial.println(WiFi.localIP());

  // init the MQTT connection
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // read the PIR sensor
  m_pir_value = digitalRead(PIR_PIN);
  if (m_pir_value == HIGH) {
    if (m_pir_state == LOW) {
      // a motion is detected
      Serial.println("INFO: Motion detected");
      publishPirSensorState();
      m_pir_state = HIGH;
    }
  } else {
    if (m_pir_state == HIGH) {
      publishPirSensorState();
      Serial.println("INFO: Motion ended");
      m_pir_state = LOW;
    }
  }
}


================================================
FILE: ha_mqtt_light/README.md
================================================
# MQTT Light - Home Assistant
A simple example to control a led connected to a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
light:
  platform: mqtt
  name: 'Office light'
  state_topic: 'office/light1/status'
  command_topic: 'office/light1/switch'
  optimistic: false
```

## Schematic
- GND - LED - Resistor 220 Ohms - D1/GPIO5

![Schematic](Schematic.png)


================================================
FILE: ha_mqtt_light/ha_mqtt_light.ino
================================================
/*
   MQTT Light for Home-Assistant - NodeMCU (ESP8266)
   https://home-assistant.io/components/light.mqtt/

   Libraries :
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient

   Sources :
    - File > Examples > ES8266WiFi > WiFiClient
    - File > Examples > PubSubClient > mqtt_auth
    - File > Examples > PubSubClient > mqtt_esp8266

   Schematic :
    - https://github.com/mertenats/open-home-automation/blob/master/ha_mqtt_light/Schematic.png
    - GND - LED - Resistor 220 Ohms - D1/GPIO5

   Configuration (HA) : 
    light:
      platform: mqtt
      name: Office light'
      state_topic: 'office/light1/status'
      command_topic: 'office/light1/switch'
      optimistic: false

   Samuel M. - v1.1 - 08.2016
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define MQTT_VERSION MQTT_VERSION_3_1_1

// Wifi: SSID and password
const char* WIFI_SSID = "[Redacted]";
const char* WIFI_PASSWORD = "[Redacted]";

// MQTT: ID, server IP, port, username and password
const PROGMEM char* MQTT_CLIENT_ID = "office_light1";
const PROGMEM char* MQTT_SERVER_IP = "[Redacted]";
const PROGMEM uint16_t MQTT_SERVER_PORT = 1883;
const PROGMEM char* MQTT_USER = "[Redacted]";
const PROGMEM char* MQTT_PASSWORD = "[Redacted]";

// MQTT: topics
const char* MQTT_LIGHT_STATE_TOPIC = "office/light1/status";
const char* MQTT_LIGHT_COMMAND_TOPIC = "office/light1/switch";

// payloads by default (on/off)
const char* LIGHT_ON = "ON";
const char* LIGHT_OFF = "OFF";

const PROGMEM uint8_t LED_PIN = 5;
boolean m_light_state = false; // light is turned off by default

WiFiClient wifiClient;
PubSubClient client(wifiClient);

// function called to publish the state of the light (on/off)
void publishLightState() {
  if (m_light_state) {
    client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_ON, true);
  } else {
    client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_OFF, true);
  }
}

// function called to turn on/off the light
void setLightState() {
  if (m_light_state) {
    digitalWrite(LED_PIN, HIGH);
    Serial.println("INFO: Turn light on...");
  } else {
    digitalWrite(LED_PIN, LOW);
    Serial.println("INFO: Turn light off...");
  }
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
  // concat the payload into a string
  String payload;
  for (uint8_t i = 0; i < p_length; i++) {
    payload.concat((char)p_payload[i]);
  }
  
  // handle message topic
  if (String(MQTT_LIGHT_COMMAND_TOPIC).equals(p_topic)) {
    // test if the payload is equal to "ON" or "OFF"
    if (payload.equals(String(LIGHT_ON))) {
      if (m_light_state != true) {
        m_light_state = true;
        setLightState();
        publishLightState();
      }
    } else if (payload.equals(String(LIGHT_OFF))) {
      if (m_light_state != false) {
        m_light_state = false;
        setLightState();
        publishLightState();
      }
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
      Serial.println("INFO: connected");
      // Once connected, publish an announcement...
      publishLightState();
      // ... and resubscribe
      client.subscribe(MQTT_LIGHT_COMMAND_TOPIC);
    } else {
      Serial.print("ERROR: failed, rc=");
      Serial.print(client.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  // init the serial
  Serial.begin(115200);

  // init the led
  pinMode(LED_PIN, OUTPUT);
  analogWriteRange(255);
  setLightState();

  // init the WiFi connection
  Serial.println();
  Serial.println();
  Serial.print("INFO: Connecting to ");
  WiFi.mode(WIFI_STA);
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("INFO: WiFi connected");
  Serial.print("INFO: IP address: ");
  Serial.println(WiFi.localIP());

  // init the MQTT connection
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}


================================================
FILE: ha_mqtt_light_arilux/LICENSE
================================================
MIT License

Copyright (c) 2017 Samuel Mertenat

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.


================================================
FILE: ha_mqtt_light_arilux/README.md
================================================
# MQTT Light - Arilux - Home Assistant
This sketch is an alternative firmware for Arilux LED controllers, based on the [WS2812FX](https://github.com/kitesurfer1404/WS2812FX) library and is compatible with `Home Assistant` through the `MQTT` protocol. 

## Configuration
To configure this sketch, you have to rename the header file `example.config.h` in `config.h`. Then, it is possible to modify the `name` and the `location` of the device or edit your Wi-Fi and MQTT credentials. Note that the device `name` and `location` are used to create the MQTT topics.

### Device location and name
```
#define DEVICE_LOCATION "bedroom"
#define DEVICE_NAME     "arilux" // also used as MQTT ID
```

### Credentials
Wi-Fi :

```
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""
```
MQTT :

```
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883
```

### Home Assistant
To integrate the light in Home Assistant, please edit and add this snippet into your configuration.

```yaml
# Example configuration.yml entry
light:
  - platform: mqtt
    schema: json
    state_topic: 'bedroom/arilux/state'
    command_topic: 'bedroom/arilux/set'
    brightness: true
    rgb: true
    white_value: true
    effect: true
    effect_list: 
      - 'Static'
      - 'Blink'
      - 'Breath'
      - 'Random Color'
      - 'Rainbow'
      - 'Fade'
      - 'Strobe'
      - 'Strobe Rainbow'
      - 'Multi Strobe'
      - 'Blink Rainbow'
      - 'Comet'
      - 'Fire Flicker'
      - 'Halloween'
```

## Flash the firmware and control
More information available [here](https://github.com/mertenats/Arilux_AL-LC0X).

Example of a returned state :
```
{
  "state" : "ON",
  "brightness" : 255,
  "color" : {
    "r" : 0,
    "g" : 250,
    "b" : 0
  },
  "white_value" : 0,
  "effect" : "Static"
}
```

## Licence
> 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.

*If you like the content of this repo, please add a star! Thank you!*


================================================
FILE: ha_mqtt_light_arilux/example.config.h
================================================
///////////////////////////////////////////////////////////////////////////////////////////////
// WIFI
///////////////////////////////////////////////////////////////////////////////////////////////

#define WIFI_SSID     ""
#define WIFI_PASSWORD ""


///////////////////////////////////////////////////////////////////////////////////////////////
// DEVICE
///////////////////////////////////////////////////////////////////////////////////////////////

#define DEVICE_LOCATION "bedroom"
#define DEVICE_NAME     "arilux" // also used as MQTT ID


///////////////////////////////////////////////////////////////////////////////////////////////
// MQTT
///////////////////////////////////////////////////////////////////////////////////////////////

#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883

// MQTT topics
//   - status state
//       < DEVICE_LOCATION >/< DEVICE_NAME >/state
//   - command 
//       < DEVICE_LOCATION >/< DEVICE_NAME >/set
//   ...
#define MQTT_AVAILABILITY_TOPIC   DEVICE_LOCATION "/" DEVICE_NAME
#define MQTT_STATE_TOPIC          DEVICE_LOCATION "/" DEVICE_NAME "/state"
#define MQTT_CMD_TOPIC            DEVICE_LOCATION "/" DEVICE_NAME "/set"

#define MQTT_STATE_ON_PAYLOAD   "ON"
#define MQTT_STATE_OFF_PAYLOAD  "OFF"

#define MQTT_AVAILABLE_PAYLOAD      "online"
#define MQTT_NOT_AVAILABLE_PAYLOAD  "offline"


///////////////////////////////////////////////////////////////////////////////////////////////
// ARILUX
///////////////////////////////////////////////////////////////////////////////////////////////

#define RED_PIN       5
#define GREEN_PIN     14
#define BLUE_PIN      12
#define WHITE_PIN     13

#define PWM_MAX       255
#define PWM_MIN       0
#define PWM_FREQUENCY 500


================================================
FILE: ha_mqtt_light_arilux/ha_mqtt_light_arilux.ino
================================================
/*
  MQTT Light for Home-Assistant - Arilux (ESP8266)
   
  Libraries :
    - ESP8266 Core : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient
    - WS2812FX :     https://github.com/kitesurfer1404/WS2812FX
    - ArduinoJson :  https://github.com/bblanchon/ArduinoJson

  Configuration for Home Assistant :
  light:
    - platform: mqtt
      schema: json
      state_topic: 'bedroom/arilux/state'
      command_topic: 'bedroom/arilux/set'
      brightness: true
      rgb: true
      white_value: true
      effect: true
      effect_list: 
        - 'Static'
        - 'Blink'
        - 'Breath'
        - 'Random Color'
        - 'Rainbow'
        - 'Fade'
        - 'Strobe'
        - 'Strobe Rainbow'
        - 'Multi Strobe'
        - 'Blink Rainbow'
        - 'Comet'
        - 'Fire Flicker'
        - 'Halloween'

   Samuel M. - v1.0 - 01.2019
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/
 
#include <ArduinoOTA.h>
#include <PubSubClient.h>   // https://github.com/knolleary/pubsubclient
#include <ArduinoJson.h>    // https://github.com/bblanchon/ArduinoJson
#include <WS2812FX.h>       // https://github.com/kitesurfer1404/WS2812FX

#include "config.h"

struct RGBW {
  uint8_t red;
  uint8_t green;
  uint8_t blue;
  uint8_t white;
  bool    isWhiteSelected;
  uint8_t brightness;
};

WiFiClient    wifiClient;
PubSubClient  mqttClient(wifiClient);
WS2812FX      ws2812fx = WS2812FX(1, NULL, NEO_RGBW);
RGBW          rgbw = {PWM_MIN,PWM_MIN,PWM_MIN,PWM_MIN,false,PWM_MAX};

long lastReconnectAttempt = 0;
bool newStateToPublish = false;

///////////////////////////////////////////////////////////////////////////////////////////////
// LIGHT
///////////////////////////////////////////////////////////////////////////////////////////////
uint8_t getRedValueFromRGB(uint32_t p_color) {return p_color >> 16;}
uint8_t getGreenValueFromRGB(uint32_t p_color) {return p_color >> 8;}
uint8_t getBlueValueFromRGB(uint32_t p_color) {return p_color;}

void customShow(void) {
  // get the current color 
  uint32 rgb = ws2812fx.getPixelColor(0);
  
  // retrieve the value for each channel
  rgbw.red =    getRedValueFromRGB(rgb);
  rgbw.green =  getGreenValueFromRGB(rgb);
  rgbw.blue =   getBlueValueFromRGB(rgb);
  
  // set each channel with the new value
  analogWrite(RED_PIN,    map(rgbw.red,   PWM_MIN, PWM_MAX, PWM_MIN, rgbw.brightness));
  analogWrite(GREEN_PIN,  map(rgbw.green, PWM_MIN, PWM_MAX, PWM_MIN, rgbw.brightness));
  analogWrite(BLUE_PIN,   map(rgbw.blue,  PWM_MIN, PWM_MAX, PWM_MIN, rgbw.brightness));
  analogWrite(WHITE_PIN,  map(rgbw.white, PWM_MIN, PWM_MAX, PWM_MIN, rgbw.brightness));
}

void setupLight(void) {
  // set the pins as output
  pinMode(RED_PIN,    OUTPUT);
  pinMode(GREEN_PIN,  OUTPUT);
  pinMode(BLUE_PIN,   OUTPUT);
  pinMode(WHITE_PIN,  OUTPUT);
  
  analogWriteFreq(PWM_FREQUENCY);
  analogWriteRange(PWM_MAX);

  ws2812fx.init();
  ws2812fx.setColor(GREEN);
  ws2812fx.stop();
  ws2812fx.setCustomShow(customShow);
}


///////////////////////////////////////////////////////////////////////////////////////////////
// MQTT
///////////////////////////////////////////////////////////////////////////////////////////////

void callback(char* p_topic, byte* p_payload, unsigned int p_length) {  
  String topic(p_topic);

  if (topic.equals(MQTT_CMD_TOPIC)) {    
    DynamicJsonBuffer dynamicJsonBuffer;
    JsonObject& root = dynamicJsonBuffer.parseObject(p_payload);
 
    // check if the payload contains a new state
    if (root.containsKey("state")) {
      if (strcmp(root["state"], MQTT_STATE_ON_PAYLOAD) == 0) {
        if (ws2812fx.isRunning() == false) {
          if (rgbw.isWhiteSelected == true) {
            rgbw.white = PWM_MAX;
          }

          ws2812fx.start();
          newStateToPublish = true;
        }
      } else if (strcmp(root["state"], MQTT_STATE_OFF_PAYLOAD) == 0) {
        if (ws2812fx.isRunning() == true) {
          if (rgbw.isWhiteSelected == true) {
            rgbw.white = PWM_MIN;
          }
          
          ws2812fx.stop();
          newStateToPublish = true;
        }
      }
    }

    // check if the payload contains a new color value
    if (root.containsKey("color")) {
      uint8_t red = root["color"]["r"];
      uint8_t green = root["color"]["g"];
      uint8_t blue = root["color"]["b"];
      
      if ((red >= PWM_MIN || red <= PWM_MAX) && (green >= PWM_MIN || green <= PWM_MAX) && (blue >= PWM_MIN || blue <= PWM_MAX)) {
        rgbw.white = PWM_MIN;
        rgbw.isWhiteSelected = false;
        ws2812fx.setMode(FX_MODE_STATIC);
        ws2812fx.setColor(red, green, blue);
        newStateToPublish = true;
      }
    }
    
    // check if the payload contains a new brightness value
    if (root.containsKey("brightness")) {
      uint8_t brightness = root["brightness"];
      
      if (brightness >= PWM_MIN || brightness <= PWM_MAX) {
        rgbw.brightness = brightness;
        newStateToPublish = true;
      }
    }

    // check if the payload contains a new white value
    if (root.containsKey("white_value")) {
      uint8_t white_value = root["white_value"];
      
      if (white_value >= PWM_MIN || white_value <= PWM_MAX) {
        rgbw.white = PWM_MAX;
        rgbw.brightness = white_value;
        rgbw.isWhiteSelected = true;
        ws2812fx.setMode(FX_MODE_STATIC);
        ws2812fx.setColor(0, 0, 0); 
        newStateToPublish = true;
      }
    }

    // check if the payload contains a new effect value
    if (root.containsKey("effect")) {
      const char* effect = root["effect"];

      // TODO check if current effect is different
      rgbw.white = PWM_MIN;
      rgbw.isWhiteSelected = false;
      newStateToPublish = true;

      if (strcmp_P(effect, (const char*)name_0) == 0) {
        ws2812fx.setMode(FX_MODE_STATIC);
      } else if (strcmp_P(effect, (const char*)name_1) == 0) {
        ws2812fx.setMode(FX_MODE_BLINK);
      } else if (strcmp_P(effect, (const char*)name_2) == 0) {
        ws2812fx.setMode(FX_MODE_BREATH);
      } else if (strcmp_P(effect, (const char*)name_8) == 0) {
        ws2812fx.setMode(FX_MODE_RANDOM_COLOR);
      } else if (strcmp_P(effect, (const char*)name_11) == 0) {
        ws2812fx.setMode(FX_MODE_RAINBOW);
      } else if (strcmp_P(effect, (const char*)name_15) == 0) {
        ws2812fx.setMode(FX_MODE_FADE);
      } else if (strcmp_P(effect, (const char*)name_26) == 0) {
        ws2812fx.setMode(FX_MODE_STROBE);
      } else if (strcmp_P(effect, (const char*)name_27) == 0) {
        ws2812fx.setMode(FX_MODE_STROBE_RAINBOW);
      } else if (strcmp_P(effect, (const char*)name_28) == 0) {
        ws2812fx.setMode(FX_MODE_MULTI_STROBE);
      } else if (strcmp_P(effect, (const char*)name_29) == 0) {
        ws2812fx.setMode(FX_MODE_BLINK_RAINBOW);
      } else if (strcmp_P(effect, (const char*)name_44) == 0) {
        ws2812fx.setMode(FX_MODE_COMET);
      } else if (strcmp_P(effect, (const char*)name_48) == 0) {
        ws2812fx.setMode(FX_MODE_FIRE_FLICKER);
      } else if (strcmp_P(effect, (const char*)name_52) == 0) {
        ws2812fx.setMode(FX_MODE_HALLOWEEN);
      } else {
        ws2812fx.setMode(FX_MODE_BLINK);
      }   
    }
  }
}

void sendState(void) {
  DynamicJsonBuffer dynamicJsonBuffer;
  JsonObject& root = dynamicJsonBuffer.createObject();
  root["state"] = ws2812fx.isRunning() ? MQTT_STATE_ON_PAYLOAD : MQTT_STATE_OFF_PAYLOAD;
  root["brightness"] = rgbw.brightness;
  JsonObject& color = root.createNestedObject("color");
  color["r"] = rgbw.red;
  color["g"] = rgbw.green;
  color["b"] = rgbw.blue;
  root["white_value"] = rgbw.white == 0 ? 0 : rgbw.brightness;
  root["effect"] = ws2812fx.getModeName(ws2812fx.getMode());//"Static";

  char tmp[128] = {0};
  root.printTo(tmp);
  mqttClient.publish(MQTT_STATE_TOPIC, tmp);
}

bool reconnect() {
  if (mqttClient.connect(DEVICE_NAME, MQTT_USERNAME, MQTT_PASSWORD, MQTT_AVAILABILITY_TOPIC, 0, 1, MQTT_NOT_AVAILABLE_PAYLOAD)) {
    mqttClient.subscribe(MQTT_CMD_TOPIC);
    mqttClient.publish(MQTT_AVAILABILITY_TOPIC, MQTT_AVAILABLE_PAYLOAD);

    sendState();
  }
  return mqttClient.connected();
}

void setupMQTT(void) {
  mqttClient.setServer(MQTT_SERVER, MQTT_SERVER_PORT);
  mqttClient.setCallback(callback);
}

///////////////////////////////////////////////////////////////////////////////////////////////
// WIFI
///////////////////////////////////////////////////////////////////////////////////////////////

void setupWiFi(void) {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

///////////////////////////////////////////////////////////////////////////////////////////////
// SETUP and LOOP
///////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  setupWiFi();

  setupMQTT();

  setupLight();
  
  ArduinoOTA.begin();
}


void loop() {
  if (!mqttClient.connected()) {
    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      if (reconnect())
        lastReconnectAttempt = 0;
    }
  } else {
    mqttClient.loop();
  }
  
  ws2812fx.service();
  
  yield();

  if (newStateToPublish) {
    newStateToPublish = false;
    sendState();  
  }

  yield();
  
  ArduinoOTA.handle();
}


================================================
FILE: ha_mqtt_light_with_WiFiManager_mDNS_and_OTA/README.md
================================================
# MQTT Light with WiFiManager, mDNS and OTA - Home Assistant
A simple example to control the built-in led connected to a NodeMCU board (ESP8266).

## Advanced features
- WiFiManager: To list the available Wifi AP and to connect to one of them.
- mDNS:        To look for the IP address and the port of the local MQTT broker.
- OTA:         To update the firmware over-the-air.

## Configuration
### MQTT broker server
Steps:
- sudo apt-get install avahi-deamon (installed by default)
- sudo nano /etc/avahi/services/mqtt.service
- Copy/paste the following lines:
```xml
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
    <name replace-wildcards="yes">%h</name>
    <service>
        <type>_mqtt._tcp</type>
        <port>1883</port>
    </service>
</service-group>
```
- sudo service avahi-daemon restart

### Home-Assistant
Warning: 'F90D5F' corresponds to the chip ID of my ESP8266.
Your chip ID is visible in the logs and corresponds to the name of the initial AP.

configuration.yaml :
```yaml
light:
  platform: mqtt
  name: 'Office light'
  state_topic: '9A6C95/light'
  command_topic: '9A6C95/light/switch'
  optimistic: false
```

## Steps
1. Select the Wifi AP provided by the ESP8266. A window is open,
2. Select "Configure WiFi",
3. Choose your Wifi AP in the list and enter your password,
4. Enter the MQTT username and password. Press "save",
5. Configure Home-Assitant.

![Schematic](Schematic.jpg)



================================================
FILE: ha_mqtt_light_with_WiFiManager_mDNS_and_OTA/ha_mqtt_light_with_WiFiManager_mDNS_and_OTA.ino
================================================
/*
  MQTT Light for Home-Assistant - NodeMCU (ESP8266)
  https://home-assistant.io/components/switch.mqtt/

  Features:
    - WiFiManager
    - mDNS
    - OTA

  Libraries:
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - WiFiManager: https://github.com/tzapu/WiFiManager
    - ESP8266mDNS: https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266mDNS
    - ArduinoJson: https://github.com/bblanchon/ArduinoJson

  Sources:
    - File > Examples > WiFiManager > AutoConnectWithFSParameters
    - File > Examples > ArduinoOTA > BasicOTA

  Configuration (HA):
  Warning: 'F90D5F' corresponds to the chip ID of my ESP
  Your chip ID is visible in the logs and the initial WIFI AP has the same name
    light:
      platform: mqtt
      name: 'Office light'
      state_topic: 'F90D5F/light'
      command_topic: 'F90D5F/light/switch'
      optimistic: false

  Samuel M. - v1.0 - 08.2016
  If you like this example, please add a star! Thank you!
  https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

/*
  WiFiManager
  Doc: https://github.com/tzapu/WiFiManager
  Moves the ESP8266 into AP mode and spins up a DNS and WebServer, IP 192.168.4.1
  Scans the available AP, asks the password for the wished AP and tries to connect to it
  Custom parameters:
    - MQTT username
    - MQTT password
  How-to:
    - Define #WIFI_MANAGER, or
    - Set manually the variables, lines 72-75
*/
#define WIFI_MANAGER

#ifdef WIFI_MANAGER
  #include <DNSServer.h>
  #include <ESP8266WebServer.h>
  #include <WiFiManager.h>
  #include <ArduinoJson.h>
  #include <FS.h>
  
  // Wifi AP: SSID: chip ID, passowrd: WIFI_PASSWORD
  const char              WIFI_PASSWORD[]           = "password";
  char                    MQTT_USERNAME[10]         = {0};
  char                    MQTT_PASSWORD[10]         = {0};
  
  // flag for saving data
  bool                    m_shouldSaveConfig        = false;
  
  void saveConfigCallback () {
    Serial.println(F("INFO: Should save config"));
    m_shouldSaveConfig = true;
  }
#else
  const PROGMEM char*     WIFI_SSID                 = "[Redacted]";
  const PROGMEM char*     WIFI_PASSWORD             = "[Redacted]";
  const PROGMEM char*     MQTT_USERNAME             = "[Redacted]";
  const PROGMEM char*     MQTT_PASSWORD             = "[Redacted]";
#endif

/*
  DNS-SD - http://www.dns-sd.org
  Soft: Avahi (Linux) or Bonjour (OS X/Windows)
  Doc:  http://linux.die.net/man/5/avahi.service
  Create a service on your machine, which hosts the MQTT broker
  On Linux:
    - sudo apt-get install avahi-deamon (installed by default)
    - sudo nano /etc/avahi/services/mqtt.service
    - Copy/paste the following lines:
        <?xml version="1.0" standalone='no'?>
        <!DOCTYPE service-group SYSTEM "avahi-service.dtd">
        <service-group>
          <name replace-wildcards="yes">%h</name>
          <service>
            <type>_mqtt._tcp</type>
            <port>1883</port>
          </service>
        </service-group>
    - sudo service avahi-daemon restart
  How-to:
    - Define #MDNS_SD, or
    - Set manually the variables, lines 106-107
*/
#define MDNS_SD

#ifdef MDNS_SD
#include <ESP8266mDNS.h>
#else
const PROGMEM char*     MQTT_SERVER_IP            = "[Redacted]";
const PROGMEM uint16_t  MQTT_SERVER_PORT          = 1883;
#endif

/*
  OTA
  Password: "1234" (line 341)
*/

#define OTA
#ifdef OTA
  #include <ESP8266mDNS.h>
  #include <WiFiUdp.h>
  #include <ArduinoOTA.h>
#endif

// MQTT
#define MQTT_VERSION MQTT_VERSION_3_1_1
char                    MQTT_CLIENT_ID[6]         = {0};

// topics: status:  <MQTT_CLIENT_ID>/light
//         command: <MQTT_CLIENT_ID>/light/switch
char                    MQTT_LIGHT_STATUS_TOPIC[13]   = {0};
char                    MQTT_LIGHT_COMMAND_TOPIC[20]  = {0};

// default payload
const char*             LIGHT_ON                  = "ON";
const char*             LIGHT_OFF                 = "OFF";

// light is turned off by default
boolean                 m_light_state             = false;

WiFiClient wifiClient;
PubSubClient client(wifiClient);

// function called to publish the state of the light (on/off)
void publishLightState() {
  if (m_light_state) {
    client.publish(MQTT_LIGHT_STATUS_TOPIC, LIGHT_ON, true);
  } else {
    client.publish(MQTT_LIGHT_STATUS_TOPIC, LIGHT_OFF, true);
  }
}

// function called to turn on/off the light
void setLightState() {
  if (m_light_state) {
    digitalWrite(BUILTIN_LED, LOW);
    Serial.println(F("INFO: Light switched on..."));
  } else {
    digitalWrite(BUILTIN_LED, HIGH);
    Serial.println(F("INFO: Light switched off..."));
  }
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
  // concat the payload into a string
  String payload;
  for (uint8_t i = 0; i < p_length; i++) {
    payload.concat((char)p_payload[i]);
  }

  // handle message topic
  if (String(MQTT_LIGHT_COMMAND_TOPIC).equals(p_topic)) {
    // test if the payload is equal to "ON" or "OFF"
    if (payload.equals(String(LIGHT_ON))) {
      if (m_light_state != true) {
        m_light_state = true;
        setLightState();
        publishLightState();
      }
    } else if (payload.equals(String(LIGHT_OFF))) {
      if (m_light_state != false) {
        m_light_state = false;
        setLightState();
        publishLightState();
      }
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
      Serial.println(F("INFO: Successfully connected to the MQTT broker"));
      publishLightState();
      client.subscribe(MQTT_LIGHT_COMMAND_TOPIC);
    } else {
      Serial.print(F("ERROR: Failed to connect to the MQTT broker"));
      delay(5000);
    }
  }
}


void setup() {
  Serial.begin(115200);

  // init the built-in led
  pinMode(BUILTIN_LED, OUTPUT);
  setLightState();

  // get the chip ID and concat this ID with the MQTT topics
  sprintf(MQTT_CLIENT_ID, "%06X", ESP.getChipId());
  sprintf(MQTT_LIGHT_STATUS_TOPIC, "%s%s", MQTT_CLIENT_ID, "/light");
  sprintf(MQTT_LIGHT_COMMAND_TOPIC, "%s%s", MQTT_CLIENT_ID, "/light/switch");

  Serial.print(F("MQTT id:\t\t "));
  Serial.println(MQTT_CLIENT_ID);

  Serial.print(F("MQTT status topic:\t "));
  Serial.println(MQTT_LIGHT_STATUS_TOPIC);

  Serial.print(F("MQTT command topic:\t "));
  Serial.println(MQTT_LIGHT_COMMAND_TOPIC);

#ifdef WIFI_MANAGER
  // clean FS, for testing
  SPIFFS.format();

  // read configuration from FS json
  Serial.println(F("INFO: mounting FS..."));

  if (SPIFFS.begin()) {
    Serial.println(F("INFO: mounted file system"));
    if (SPIFFS.exists("/config.json")) {
      Serial.println(F("INFO: reading config file"));
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println(F("INFO: opened config file"));
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println(F("\nINFO: parsed json"));
          strcpy(MQTT_USERNAME, json["mqtt_username"]);
          strcpy(MQTT_PASSWORD, json["mqtt_password"]);
        } else {
          Serial.println(F("ERROR: failed to load json config"));
        }
      }
    }
  } else {
    Serial.println(F("ERROR: failed to mount FS"));
  }

  WiFiManagerParameter custom_mqtt_username("mqtt_username", "MQTT username", MQTT_USERNAME, 15);
  WiFiManagerParameter custom_mqtt_password("mqtt_password", "MQTT password", MQTT_PASSWORD, 15, "type=\"password\"");

  // WiFiManager, local intialization
  WiFiManager wifiManager;

  // set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);

  // add all the parameters
  wifiManager.addParameter(&custom_mqtt_username);
  wifiManager.addParameter(&custom_mqtt_password);

  // reset settings, for testing
  wifiManager.resetSettings();

  // disable the debug output
  wifiManager.setDebugOutput(false);

  // fetches ssid and pass and tries to connect
  // if it does not connect it starts an access point with the specified name
  if (!wifiManager.autoConnect(MQTT_CLIENT_ID, WIFI_PASSWORD)) {
    Serial.println(F("Error: Failed to connect and hit timeout"));
    delay(3000);
    ESP.reset();
    delay(5000);
  }

  // if you get here you have connected to the WiFi
  Serial.println(F("INFO: Successfully connected to the Wifi AP"));

  // read updated parameters
  strcpy(MQTT_USERNAME, custom_mqtt_username.getValue());
  strcpy(MQTT_PASSWORD, custom_mqtt_password.getValue());

  // save the custom parameters to FS
  if (m_shouldSaveConfig) {
    Serial.println(F("INFO: saving config"));
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["mqtt_username"] = MQTT_USERNAME;
    json["mqtt_password"] = MQTT_PASSWORD;

    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println(F("ERROR: failed to open config file for writing"));
    }
    json.printTo(Serial);
    json.printTo(configFile);
    Serial.println();
    configFile.close();
  }

  Serial.print(F("INFO: MQTT username: "));
  Serial.println(MQTT_USERNAME);

  Serial.print(F("INFO: MQTT password: "));
  Serial.println(MQTT_PASSWORD);

  Serial.print(F("INFO: Local IP address: "));
  Serial.println(WiFi.localIP());
#else
  WiFi.hostname(MQTT_CLIENT_ID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(250);
  }
  Serial.println(F("INFO: Successfully connected to the Wifi AP"));
  Serial.print(F("INFO: Local IP address: "));
  Serial.println(WiFi.localIP());
#endif

  delay(500);

#ifdef OTA
  // set port to 8266
  //ArduinoOTA.setPort(8266);

  // set hostname
  sprintf(MQTT_CLIENT_ID, "%06X", ESP.getChipId());
  ArduinoOTA.setHostname(MQTT_CLIENT_ID);

  // set a password
  ArduinoOTA.setPassword((const char *)"1234");

  ArduinoOTA.onStart([]() {
    Serial.println(F("INFO: Start OTA"));
  });
  ArduinoOTA.onEnd([]() {
    Serial.println(F("INFO: End OTA"));
    ESP.reset();
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("INFO: Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println(F("ERROR: OTA auth Failed"));
    else if (error == OTA_BEGIN_ERROR) Serial.println(F("ERROR: OTA begin Failed"));
    else if (error == OTA_CONNECT_ERROR) Serial.println(F("ERROR: OTA connect Failed"));
    else if (error == OTA_RECEIVE_ERROR) Serial.println(F("ERROR: OTA receive Failed"));
    else if (error == OTA_END_ERROR) Serial.println(F("ERROR: OTA end Failed"));
  });
  ArduinoOTA.begin();
#endif

  delay(500);

#ifdef MDNS_SD
  if (!MDNS.begin(MQTT_CLIENT_ID)) {
    Serial.println(F("ERROR: Setting up MDNS responder!"));
  }
  Serial.println(F("INFO: mDNS responder started"));
  Serial.println(F("INFO: Sending mDNS query"));
  int n = MDNS.queryService("mqtt", "tcp"); // Send out query for the MQTT service
  Serial.println(F("INFO: mDNS query done"));
  if (n == 0) {
    Serial.println(F("ERROR: No services found"));
  } else if (n > 1) {
    Serial.println(F("ERROR: Multiple services found, remove the unnecessary mDNS services"));
  } else {
    Serial.print(F("INFO: MQTT broker IP address: "));
    Serial.print(MDNS.IP(0));
    Serial.print(F(":"));
    Serial.println(MDNS.port(0));
    client.setServer(MDNS.IP(0), int(MDNS.port(0)));
  }
#else
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
#endif

  client.setCallback(callback);
}

void loop() {
  // put your main code here, to run repeatedly

  // MQTT
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  ArduinoOTA.handle();
}


================================================
FILE: ha_mqtt_light_with_brightness/README.md
================================================
# MQTT Light with brightness support - Home Assistant
A simple example to control a led connected to a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
light:
  - platform: mqtt
    name: 'Office light'
    state_topic: 'office/light'
    command_topic: 'office/light/switch'
    brightness_state_topic: 'office/light/brightness'
    brightness_command_topic: 'office/light/brightness/set'
    optimistic: false
```

## Schematic
- GND - Resistor 220 Ohms - LED leg 1
- D1/GPIO5 - LED leg 2 (longuest one)

![Schematic](Schematic.png)


================================================
FILE: ha_mqtt_light_with_brightness/ha_mqtt_light_with_brightness.ino
================================================
/*
   MQTT Light (with brightness) for Home-Assistant - NodeMCU (ESP8266)
   https://home-assistant.io/components/light.mqtt/

   Libraries :
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient

   Schematic :
    - https://github.com/mertenats/open-home-automation/blob/master/ha_mqtt_rgb_light/Schematic.png
    - GND - Resistor 220 Ohms - LED leg 1
    - D1/GPIO5 - LED leg 2 (longuest one)

   Configuration (HA) : 
    light:
      platform: mqtt
      name: 'Office light'
      state_topic: 'office/light'
      command_topic: 'office/light/switch'
      brightness_state_topic: 'office/light/brightness'
      brightness_command_topic: 'office/light/brightness/set'
      optimistic: false

   Samuel M. - v1.0 - 08.2016
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define MQTT_VERSION MQTT_VERSION_3_1_1

// Wifi: SSID and password
const char*             WIFI_SSID         = "[Redacted]";
const char*             WIFI_PASSWORD     = "[Redacted]";

// MQTT: ID, server IP, port, username and password
const PROGMEM char*     MQTT_CLIENT_ID    = "office_light";
const PROGMEM char*     MQTT_SERVER_IP    = "[Redacted]";
const PROGMEM uint16_t  MQTT_SERVER_PORT  = 1883;
const PROGMEM char*     MQTT_USER         = "[Redacted]";
const PROGMEM char*     MQTT_PASSWORD     = "[Redacted]";

// MQTT: topics
// state
const PROGMEM char*     MQTT_LIGHT_STATE_TOPIC                = "office/light";
const PROGMEM char*     MQTT_LIGHT_COMMAND_TOPIC              = "office/light/switch";

// brightness
const PROGMEM char*     MQTT_LIGHT_BRIGHTNESS_STATE_TOPIC     = "office/light/brightness";
const PROGMEM char*     MQTT_LIGHT_BRIGHTNESS_COMMAND_TOPIC   = "office/light/brightness/set";

// payloads by default (on/off)
const PROGMEM char*     LIGHT_ON          = "ON";
const PROGMEM char*     LIGHT_OFF         = "OFF";

// variables used to store the statea and the brightness of the light
boolean   m_light_state       = false;
uint8_t   m_light_brightness  = 255;

// pin used for the led (PWM)
const PROGMEM uint8_t LIGHT_PIN = 4;

// buffer used to send/receive data with MQTT
const uint8_t MSG_BUFFER_SIZE = 20;
char m_msg_buffer[MSG_BUFFER_SIZE]; 

WiFiClient wifiClient;
PubSubClient client(wifiClient);

// function called to adapt the brightness and the state of the led
void setLightState() {
  if (m_light_state) {
    analogWrite(LIGHT_PIN, m_light_brightness);
    Serial.print("INFO: Brightness: ");
    Serial.println(m_light_brightness);
  } else {
    analogWrite(LIGHT_PIN, 0);
    Serial.println("INFO: Turn light off");
  }
}

// function called to publish the state of the led (on/off)
void publishLightState() {
  if (m_light_state) {
    client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_ON, true);
  } else {
    client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_OFF, true);
  }
}

// function called to publish the brightness of the led
void publishLightBrightness() {
  snprintf(m_msg_buffer, MSG_BUFFER_SIZE, "%d", m_light_brightness);
  client.publish(MQTT_LIGHT_BRIGHTNESS_STATE_TOPIC, m_msg_buffer, true);
}


// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
  // concat the payload into a string
  String payload;
  for (uint8_t i = 0; i < p_length; i++) {
    payload.concat((char)p_payload[i]);
  }
  // handle message topic
  if (String(MQTT_LIGHT_COMMAND_TOPIC).equals(p_topic)) {
    // test if the payload is equal to "ON" or "OFF"
    if (payload.equals(String(LIGHT_ON))) {
      if (m_light_state != true) {
        m_light_state = true;
        setLightState();
        publishLightState();
      }
    } else if (payload.equals(String(LIGHT_OFF))) {
      if (m_light_state != false) {
        m_light_state = false;
        setLightState();
        publishLightState();
      }
    }
  } else if (String(MQTT_LIGHT_BRIGHTNESS_COMMAND_TOPIC).equals(p_topic)) {
    uint8_t brightness = payload.toInt();
    if (brightness < 0 || brightness > 255) {
      // do nothing...
      return;
    } else {
      m_light_brightness = brightness;
      setLightState();
      publishLightBrightness();
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
      Serial.println("\nINFO: connected");
      
      // Once connected, publish an announcement...
      // publish the initial values
      publishLightState();
      publishLightBrightness();

      // ... and resubscribe
      client.subscribe(MQTT_LIGHT_COMMAND_TOPIC);
      client.subscribe(MQTT_LIGHT_BRIGHTNESS_COMMAND_TOPIC);
    } else {
      Serial.print("ERROR: failed, rc=");
      Serial.print(client.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  // init the serial
  Serial.begin(115200);

  // init the led
  pinMode(LIGHT_PIN, OUTPUT);
  analogWriteRange(255);
  setLightState();

  // init the WiFi connection
  Serial.println();
  Serial.println();
  Serial.print("INFO: Connecting to ");
  WiFi.mode(WIFI_STA);
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("INFO: WiFi connected");
  Serial.print("INFO: IP address: ");
  Serial.println(WiFi.localIP());

  // init the MQTT connection
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}


================================================
FILE: ha_mqtt_multisensor/Configuration/example.binary_sensor.yaml
================================================
# Example configuration.yaml entry
binary_sensor:
  - platform: mqtt
    name: 'Motion'
    state_topic: '<CHIP_ID>/sensor/motion'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
    device_class: motion
  - platform: mqtt
    name: 'Door'
    state_topic: '<CHIP_ID>/sensor/door'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
    device_class: opening
  - platform: mqtt
    name: 'Button'
    state_topic: '<CHIP_ID>/sensor/button'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'


================================================
FILE: ha_mqtt_multisensor/Configuration/example.group.yaml
================================================
group:
  multisensor:
    name: 'Multisensor'
    entities:
      - sensor.temperature
      - sensor.humidity
      - sensor.luminosity
      - binary_sensor.motion
      - binary_sensor.door
      - binary_sensor.button


================================================
FILE: ha_mqtt_multisensor/Configuration/example.sensor.yaml
================================================
# Example configuration.yml entry
sensor:
  - platform: mqtt
    name: 'Temperature'
    state_topic: '<CHIP_ID>/sensor/temperature'
    unit_of_measurement: '°C'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
  - platform: mqtt
    name: 'Humidity'
    state_topic: '<CHIP_ID>/sensor/humidity'
    unit_of_measurement: '%'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
  - platform: mqtt
    name: 'Luminosity'
    state_topic: '<CHIP_ID>/sensor/lux'
    unit_of_measurement: 'lux'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'


================================================
FILE: ha_mqtt_multisensor/LICENSE
================================================
MIT License

Copyright (c) 2017 Samuel Mertenat

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.


================================================
FILE: ha_mqtt_multisensor/MultiSensor.cpp
================================================
#include "Arduino.h"
#include "MultiSensor.h"

#if defined(DHT_SENSOR)
// https://github.com/adafruit/Adafruit_Sensor
// https://github.com/adafruit/DHT-sensor-library
#include "DHT.h"
#define DHTTYPE DHT22
DHT dht(DHT_PIN, DHTTYPE);
#endif

#if defined(SHT_SENSOR)
// https://github.com/Sensirion/arduino-sht
#include <Wire.h>
#include "SHTSensor.h"
SHTSensor sht;
#endif

volatile uint8_t evt = NO_SENSOR_EVT;    

///////////////////////////////////////////////////////////////////////////
//  ISRs
///////////////////////////////////////////////////////////////////////////

#if defined(DOOR_SENSOR)
void doorSensorISR(void) {
  evt = DOOR_SENSOR_EVT;
}
#endif

#if defined(MOTION_SENSOR)
void motionSensorISR(void) {
  evt = MOTION_SENSOR_EVT;
}
#endif

#if defined(BUTTON_SENSOR)
void buttonSensorISR(void) {
  static unsigned long lastButtonSensorInterrupt = 0;
  unsigned long currentButtonSensorInterrupt = millis();
  if (currentButtonSensorInterrupt - lastButtonSensorInterrupt > 500)
    evt = BUTTON_SENSOR_EVT;
  lastButtonSensorInterrupt = currentButtonSensorInterrupt;
}
#endif


///////////////////////////////////////////////////////////////////////////
//  Constructor, init(), handleEvt() & loop()
///////////////////////////////////////////////////////////////////////////
MultiSensor::MultiSensor(void) {}

void MultiSensor::init(void) {
#if defined(DOOR_SENSOR)
  pinMode(DOOR_SENSOR, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(DOOR_SENSOR), doorSensorISR, CHANGE);
  this->_doorState = this->_readDoorState();
#endif
#if defined(MOTION_SENSOR)
  pinMode(MOTION_SENSOR_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(MOTION_SENSOR_PIN), motionSensorISR, CHANGE);
  this->_motionState = digitalRead(MOTION_SENSOR_PIN);
#endif
#if defined(LDR_SENSOR)
  pinMode(LDR_PIN, INPUT);
  this->_ldrValue = analogRead(LDR_PIN);
#endif
#if defined(DHT_SENSOR)
  dht.begin();
  delay(2000);
  this->_readDHTTemperature();
  this->_readDHTHumidity();
#endif
#if defined(SHT_SENSOR)
  Wire.begin(SHT_SCL_PIN, SHT_SDA_PIN);
  sht.init();
  sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); 
  this->_readSHTTemperature();
  this->_readSHTHumidity();
#endif
#if defined(BUTTON_SENSOR)
  pinMode(BUTTON_SENSOR, INPUT);
  attachInterrupt(digitalPinToInterrupt(BUTTON_SENSOR), buttonSensorISR, CHANGE);
#endif
}

void MultiSensor::handleEvt(void) {
  switch(evt) {
    case NO_SENSOR_EVT:
      break;
#if defined(DOOR_SENSOR)
    case DOOR_SENSOR_EVT:
      if (this->_readDoorState() != this->_doorState) {
        this->_doorState = !this->_doorState;
        this->_callback(DOOR_SENSOR_EVT);
      }
      evt = NO_SENSOR_EVT;
      break;
#endif
#if defined(MOTION_SENSOR)
    case MOTION_SENSOR_EVT:
      if (digitalRead(MOTION_SENSOR_PIN) != this->_motionState) {
        this->_motionState = !this->_motionState;
        this->_callback(MOTION_SENSOR_EVT);
      }
      evt = NO_SENSOR_EVT;
      break;
#endif
#if defined(BUTTON_SENSOR)
    case BUTTON_SENSOR_EVT:
      this->_buttonState = !this->_buttonState;
      this->_callback(BUTTON_SENSOR_EVT);
      evt = NO_SENSOR_EVT;
      break;
#endif
#if defined(LDR_SENSOR)
    case LDR_SENSOR_EVT:
      this->_callback(LDR_SENSOR_EVT);
      evt = NO_SENSOR_EVT;
      break;
#endif
#if defined(DHT_SENSOR)
    case DHT_TEMPERATURE_SENSOR_EVT:
      this->_callback(DHT_TEMPERATURE_SENSOR_EVT);
      evt = NO_SENSOR_EVT;
      break;
    case DHT_HUMIDITY_SENSOR_EVT:
      this->_callback(DHT_HUMIDITY_SENSOR_EVT);
      evt = NO_SENSOR_EVT;
      break;
#endif
#if defined(SHT_SENSOR)
    case SHT_TEMPERATURE_SENSOR_EVT:
      this->_callback(SHT_TEMPERATURE_SENSOR_EVT);
      evt = NO_SENSOR_EVT;
      break;
    case SHT_HUMIDITY_SENSOR_EVT:
      this->_callback(SHT_HUMIDITY_SENSOR_EVT);
      evt = NO_SENSOR_EVT;
      break;
#endif
  }
}

void MultiSensor::loop(void) {
  this->handleEvt();
  
#if defined(LDR_SENSOR)
  static unsigned long lastLdrSensorMeasure = 0;
  if (lastLdrSensorMeasure + LDR_MEASURE_INTERVAL <= millis()) {
    lastLdrSensorMeasure = millis();
    uint16_t currentLdrValue = analogRead(LDR_PIN);
    if (currentLdrValue <= this->_ldrValue - LDR_OFFSET_VALUE || currentLdrValue >= this->_ldrValue + LDR_OFFSET_VALUE) {
      this->_ldrValue = currentLdrValue;
      evt = LDR_SENSOR_EVT;
      return;
    }
  }
#endif

#if defined(DHT_SENSOR)
  static unsigned long lastDHTTemperatureSensorMeasure = 0;
  if (lastDHTTemperatureSensorMeasure + DHT_MEASURE_INTERVAL <= millis()) {
    lastDHTTemperatureSensorMeasure = millis();
    float currentDHTTemperature = this->_readDHTTemperature();
    if (currentDHTTemperature <= this->_DHTTemperature - DHT_TEMPERATURE_OFFSET_VALUE || currentDHTTemperature >= this->_DHTTemperature + DHT_TEMPERATURE_OFFSET_VALUE) {
      this->_DHTTemperature = currentDHTTemperature;
      evt = DHT_TEMPERATURE_SENSOR_EVT;
      return;
    }
  }
  
  static unsigned long lastDHTHumiditySensorMeasure = 0;
  if (lastDHTHumiditySensorMeasure + DHT_MEASURE_INTERVAL <= millis()) {
    lastDHTHumiditySensorMeasure = millis();
    float currentDHTHumidity = this->_readDHTHumidity();
    if (currentDHTHumidity <= this->_DHTHumidity - DHT_HUMIDITY_OFFSET_VALUE || currentDHTHumidity >= this->_DHTHumidity + DHT_HUMIDITY_OFFSET_VALUE) {
      this->_DHTHumidity = currentDHTHumidity;
      evt = DHT_HUMIDITY_SENSOR_EVT;
      return;
    }
  }
#endif
#if defined(SHT_SENSOR)
  static unsigned long lastSHTTemperatureSensorMeasure = 0;
  if (lastSHTTemperatureSensorMeasure + SHT_MEASURE_INTERVAL <= millis()) {
    lastSHTTemperatureSensorMeasure = millis();
    float currentSHTTemperature = this->_readSHTTemperature();
    if (currentSHTTemperature <= this->_SHTTemperature - SHT_TEMPERATURE_OFFSET_VALUE || currentSHTTemperature >= this->_SHTTemperature + SHT_TEMPERATURE_OFFSET_VALUE) {
      this->_SHTTemperature = currentSHTTemperature;
      evt = SHT_TEMPERATURE_SENSOR_EVT;
      return;
    }
  }
  
  static unsigned long lastSHTHumiditySensorMeasure = 0;
  if (lastSHTHumiditySensorMeasure + SHT_MEASURE_INTERVAL <= millis()) {
    lastSHTHumiditySensorMeasure = millis();
    float currentSHTHumidity = this->_readSHTHumidity();
    if (currentSHTHumidity <= this->_SHTHumidity - SHT_HUMIDITY_OFFSET_VALUE || currentSHTHumidity >= this->_SHTHumidity + SHT_HUMIDITY_OFFSET_VALUE) {
      this->_SHTHumidity = currentSHTHumidity;
      evt = SHT_HUMIDITY_SENSOR_EVT;
      return;
    }
  }
#endif
}


///////////////////////////////////////////////////////////////////////////
//  setCallback()
///////////////////////////////////////////////////////////////////////////
void MultiSensor::setCallback(void (*callback)(uint8_t)) {
  this->_callback = callback;
}


///////////////////////////////////////////////////////////////////////////
//  Getters 
///////////////////////////////////////////////////////////////////////////
#if defined(DOOR_SENSOR)
bool MultiSensor::_readDoorState(void) {
  return digitalRead(DOOR_SENSOR);
}

bool MultiSensor::getDoorState(void) {
  return this->_doorState;
}
#endif

#if defined(MOTION_SENSOR)
bool MultiSensor::getMotionState(void) {
  return this->_motionState;
}
#endif

#if defined(LDR_SENSOR)
uint16_t MultiSensor::getLux(void) {
  // http://forum.arduino.cc/index.php?topic=37555.0
  // https://forum.arduino.cc/index.php?topic=185158.0
  float volts = this->_ldrValue * REFERENCE_VOLTAGE / ADC_PRECISION;
  float amps = volts / LDR_RESISTOR_VALUE;
  float lux = amps * 1000000 * 2.0;
  return uint16_t(lux);
}
#endif


#if defined(DHT_SENSOR)
float MultiSensor::_readDHTTemperature(void) {
  float temperature = dht.readTemperature();

  if (isnan(temperature)) {
    return this->_DHTTemperature;
  }
  return temperature;
}

float MultiSensor::_readDHTHumidity(void) {
  float humidity = dht.readHumidity();

  if (isnan(humidity)) {
    return this->_DHTHumidity;
  }
  return humidity;
}

float MultiSensor::getDHTTemperature(void) {
  return this->_DHTTemperature;
}

float MultiSensor::getDHTHumidity(void) {
  return this->_DHTHumidity;
}
#endif


#if defined(SHT_SENSOR)
float MultiSensor::_readSHTTemperature(void) {
  float temperature = sht.getTemperature();
  
  if (isnan(temperature)) {
    return this->_SHTTemperature;
  }
  return temperature;
}

float MultiSensor::_readSHTHumidity(void) {
  float humidity = sht.getHumidity();

  if (isnan(humidity)) {
    return this->_SHTHumidity;
  }
  return humidity;
}

float MultiSensor::getSHTTemperature(void) {
  return this->_SHTTemperature;
}

float MultiSensor::getSHTHumidity(void) {
  return this->_SHTHumidity;
}
#endif


#if defined(BUTTON_SENSOR)
bool MultiSensor::getButtonState(void) {
  return this->_buttonState;
}
#endif


================================================
FILE: ha_mqtt_multisensor/MultiSensor.h
================================================
#ifndef MultiSensor_h
#define MultiSensor_h

#include "Arduino.h"
#include "config.h"

#define NO_SENSOR_EVT                 0
#if defined(DOOR_SENSOR)
#define DOOR_SENSOR_EVT               1
#endif
#if defined(MOTION_SENSOR)
#define MOTION_SENSOR_EVT             2
#endif
#if defined(LDR_SENSOR)
#define LDR_SENSOR_EVT                3
#endif
#if defined(DHT_SENSOR)
#define DHT_TEMPERATURE_SENSOR_EVT    4
#define DHT_HUMIDITY_SENSOR_EVT       5
#endif
#if defined(SHT_SENSOR)
#define SHT_TEMPERATURE_SENSOR_EVT    6
#define SHT_HUMIDITY_SENSOR_EVT       7
#endif
#if defined(BUTTON_SENSOR)
#define BUTTON_SENSOR_EVT             8
#endif

class MultiSensor {
  public:
    MultiSensor(void);
    void init(void);
    void loop(void);
    void setCallback(void (*callback)(uint8_t));
    
#if defined(DOOR_SENSOR)
    bool getDoorState(void);
#endif
#if defined(MOTION_SENSOR)
    bool getMotionState(void);
#endif
#if defined(LDR_SENSOR)
    uint16_t getLux(void);
#endif
#if defined(DHT_SENSOR)
    float getDHTTemperature(void);
    float getDHTHumidity(void);
#endif
#if defined(SHT_SENSOR)
    float getSHTTemperature(void);
    float getSHTHumidity(void);
#endif
#if defined(BUTTON_SENSOR)
    bool getButtonState(void);
#endif
  private:
    void (*_callback)(uint8_t);
    void handleEvt(void);
#if defined(DOOR_SENSOR)
    bool _readDoorState(void);
    bool _doorState = false;
#endif
#if defined(MOTION_SENSOR)
    bool _motionState = false;
#endif
#if defined(LDR_SENSOR)
    uint16_t _ldrValue = 0;
#endif
#if defined(DHT_SENSOR)
    float _readDHTTemperature(void);
    float _readDHTHumidity(void);
    float _DHTTemperature = 0;
    float _DHTHumidity = 0;
#endif
#if defined(SHT_SENSOR)
    float _readSHTTemperature(void);
    float _readSHTHumidity(void);
    float _SHTTemperature = 0;
    float _SHTHumidity = 0;
#endif
#if defined(BUTTON_SENSOR)
    bool _buttonState = false;
#endif
};
#endif


================================================
FILE: ha_mqtt_multisensor/README.md
================================================
# MQTT - MultiSensor - Home Assistant
A full example describing how to monitor your environment with an ESP8266, the MQTT protocol and Home Assistant.

## Features
- Temperature (DHT22, SHT3X)
- Humidity (DHT22, SHT3X)
- Luminosity (Photoresistor, TEMT6000)
- Motion (AM312, RCWL 0516)
- Door/Window state ON/OFF
- Button state ON/OFF

## Schematic
### DHT22 sensor
Temperature and Humidity sensor. Schematic available [here](https://github.com/mertenats/Open-Home-Automation/tree/master/ha_mqtt_sensor_dht22).

- DHT22 leg 1 - VCC
- DHT22 leg 2 - D2 - Resistor 4.7K Ohms - GND
- DHT22 leg 4 - GND

### SHT3X sensor
Temperature and Humidity sensor.

- SHT3X VCC - VCC
- SHT3X GND - GND
- SHT3X SCL - D2
- SHT3X SDA - D3

### Photoresistor
Luminosity sensor. Schematic available [here](https://github.com/mertenats/Open-Home-Automation/tree/master/ha_mqtt_sensor_photocell).

- Photocell leg 1 - VCC
- Photocell leg 2 - A0 - Resistor 10K Ohms - GND

### TEMT6000
Luminosity sensor.

- TEMT6000 VCC - VCC
- TEMT6000 GND - GND
- TEMT6000 OUT - A0

### AM312 sensor
Motion sensor. Schematic available [here](https://github.com/mertenats/Open-Home-Automation/tree/master/ha_mqtt_binary_sensor_pir).

- PIR leg 1 - VCC
- PIR leg 2 - D7
- PIR leg 3 - GND

### RCWL 0516 sensor
Motion sensor.

- RCWL 0516 3v3 - VCC
- RCWL 0516 GND - GND
- RCWL 0516 OUT - D7

### Magnet
Door/Window state sensor. Schematic available [here](https://github.com/mertenats/Open-Home-Automation/tree/master/ha_mqtt_binary_sensor_door).

- Door sensor leg 1 - D3
- Door sensor leg 2 - GND

### Button
Button ON/OFF. Schematic available [here](https://github.com/mertenats/Open-Home-Automation/tree/master/ha_mqtt_switch).

- Switch leg 1 - VCC
- Switch leg 2 - D1 - Resistor 10K Ohms - GND

![Schematic](Images/Schematic.jpg)

## Configuration
To configure this sketch, you have to rename the header file `example.config.h` in `config.h`. Then, it is possible to enable/disable sensors in the `HARDWARE SECTION` or edit your Wi-Fi and MQTT credentials in the `SOFTWARE SECTION`.

### Sensors
```
// Door sensor
#define DOOR_SENSOR                     D3

// Motion sensor
#define MOTION_SENSOR                      D7

// Photoresistor sensor
#define LDR_SENSOR                      A0

// Temperature and humidity sensor (DHT22)
#define DHT_SENSOR                  D2

// Button
#define BUTTON_SENSOR                   D1
```

### Credentials
```
// Wi-Fi credentials
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""

// MQTT
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883
```

### Home Assistant
To add the motion sensor, the door/window state sensor and the button to Home Assistant, please edit and add this snippet into your configuration.

```yaml
# Example configuration.yaml entry
binary_sensor:
  - platform: mqtt
    name: 'Motion'
    state_topic: '<CHIP_ID>/sensor/motion'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
    device_class: motion
  - platform: mqtt
    name: 'Door'
    state_topic: '<CHIP_ID>/sensor/door'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
    device_class: opening
  - platform: mqtt
    name: 'Button'
    state_topic: '<CHIP_ID>/sensor/button'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
```

To add the temperature and humidity sensor and the luminosity sensor, please edit and add this snippet into your configuration.

```yaml
# Example configuration.yml entry
sensor:
  - platform: mqtt
    name: 'Temperature'
    state_topic: '<CHIP_ID>/sensor/temperature'
    unit_of_measurement: '°C'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
  - platform: mqtt
    name: 'Humidity'
    state_topic: '<CHIP_ID>/sensor/humidity'
    unit_of_measurement: '%'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
  - platform: mqtt
    name: 'Luminosity'
    state_topic: '<CHIP_ID>/sensor/lux'
    unit_of_measurement: 'lux'
    availability_topic: '<CHIP_ID>/status'
    payload_available: 'online'
    payload_not_available: 'offline'
```

## Licence
> 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.

*If you like the content of this repo, please add a star! Thank you!*


================================================
FILE: ha_mqtt_multisensor/example.config .h
================================================
///////////////////////////////////////////////////////////////////////////
//  CONFIGURATION - HARDWARE
///////////////////////////////////////////////////////////////////////////
// Door sensor
#define DOOR_SENSOR                     D3
#if defined(DOOR_SENSOR)
#define DOOR_SENSOR_NAME                "door" // used for the MQTT topic
#endif

// Motion sensor
//   - AM312
//   - RCWL 0516
#define MOTION_SENSOR
#if defined(MOTION_SENSOR)
#define MOTION_SENSOR_NAME                 "motion"
#define MOTION_SENSOR_PIN                  D7
#endif

// Ambient light sensor
//   - Photoresistor
//   - TEMT6000 sensor
// Do not add a 10 kOhms resistor if you're using the TEMT6000
// sensor (already present on the PCB)
//#define LDR_SENSOR   
#define LDR_SENSOR
#if defined(LDR_SENSOR)
#define LDR_SENSOR_NAME                 "lux"
#define LDR_OFFSET_VALUE                25
#define LDR_MEASURE_INTERVAL            15000   // [ms]
#define REFERENCE_VOLTAGE               3.3     // [v]
#define ADC_PRECISION                   1024.0  // 10 bits
#define LDR_RESISTOR_VALUE              10000.0  // [Ohms]
#define LDR_PIN                         A0
#endif

// Temperature and humidity sensor (DHT22)
//#define DHT_SENSOR
#if defined(DHT_SENSOR)
#define DHT_TEMPERATURE_SENSOR_NAME     "temperature"
#define DHT_HUMIDITY_SENSOR_NAME        "humidity"
#define DHT_TEMPERATURE_OFFSET_VALUE    0.2   // [°C]
#define DHT_HUMIDITY_OFFSET_VALUE       0.5   // [%]
#define DHT_MEASURE_INTERVAL            30000 // [ms]
#define DHT_PIN                         D2
#endif

// Temperature and humidity sensor (Sensirion SHT3X)
#define SHT_SENSOR
#if defined(SHT_SENSOR)
#define SHT_TEMPERATURE_SENSOR_NAME   "temperature"
#define SHT_HUMIDITY_SENSOR_NAME      "humidity"
#define SHT_TEMPERATURE_OFFSET_VALUE  0.2   // [°C]
#define SHT_HUMIDITY_OFFSET_VALUE     0.5   // [%]
#define SHT_MEASURE_INTERVAL          30000 // [ms]
#define SHT_SDA_PIN                   D3
#define SHT_SCL_PIN                   D2
#endif

// Button
#define BUTTON_SENSOR                   D1
#if defined(BUTTON_SENSOR)
#define BUTTON_SENSOR_NAME              "button"
#endif


///////////////////////////////////////////////////////////////////////////
//  CONFIGURATION - SOFTWARE
///////////////////////////////////////////////////////////////////////////
// Debug output
#define DEBUG_SERIAL

// Wi-Fi credentials
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""

// Over-the-Air update
#define OTA
#define OTA_HOSTNAME  "MultiSensor"  // hostname esp8266-[ChipID] by default
//#define OTA_PASSWORD  "password"  // no password by default
//#define OTA_PORT      8266        // port 8266 by default

// MQTT
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883

#define MQTT_CONNECTION_TIMEOUT 5000 // [ms]

#define MQTT_AVAILABILITY_TOPIC_TEMPLATE  "%s/status" // MQTT availability: online/offline
#define MQTT_SENSOR_TOPIC_TEMPLATE        "%s/sensor/%s"

#define MQTT_PAYLOAD_ON   "ON"
#define MQTT_PAYLOAD_OFF  "OFF"


================================================
FILE: ha_mqtt_multisensor/ha_mqtt_multisensor.ino
================================================
#include <ESP8266WiFi.h>
#include "MultiSensor.h"
#include <PubSubClient.h> // https://github.com/knolleary/pubsubclient
#if defined(OTA)
#include <ArduinoOTA.h>
#endif

#if defined(DEBUG_SERIAL)
#define     DEBUG_PRINT(x)    Serial.print(x)
#define     DEBUG_PRINTLN(x)  Serial.println(x)
#else
#define     DEBUG_PRINT(x)
#define     DEBUG_PRINTLN(x)
#endif

MultiSensor   ms;
WiFiClient    wifiClient;
PubSubClient  mqttClient(wifiClient);


///////////////////////////////////////////////////////////////////////////
//   WiFi
///////////////////////////////////////////////////////////////////////////
/*
   Function called to handle WiFi events
*/
void handleWiFiEvent(WiFiEvent_t event) {
  switch (event) {
    case WIFI_EVENT_STAMODE_GOT_IP:
      DEBUG_PRINTLN(F("INFO: Connection successful to the Wi-Fi AP"));
      DEBUG_PRINT(F("INFO: IP address: "));
      DEBUG_PRINTLN(WiFi.localIP());
      break;
    case WIFI_EVENT_STAMODE_DISCONNECTED:
      DEBUG_PRINTLN(F("ERROR: Connection lost from the Wi-Fi AP"));
      /*
         TODO: Doing something smarter than rebooting the device
      */
      delay(5000);
      ESP.restart();
      break;
    default:
      DEBUG_PRINT(F("INFO: WiFi event: "));
      DEBUG_PRINTLN(event);
      break;
  }
}

/*
   Function called to setup the connection to the WiFi AP
*/
void setupWiFi() {
  DEBUG_PRINT(F("INFO: WiFi connecting to: "));
  DEBUG_PRINTLN(WIFI_SSID);

  delay(10);

  WiFi.mode(WIFI_STA);
  WiFi.onEvent(handleWiFiEvent);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  randomSeed(micros());
}


///////////////////////////////////////////////////////////////////////////
//   OTA
///////////////////////////////////////////////////////////////////////////
#if defined(OTA)
/*
   Function called to setup OTA updates
*/
void setupOTA() {
#if defined(OTA_HOSTNAME)
  ArduinoOTA.setHostname(OTA_HOSTNAME);
  DEBUG_PRINT(F("INFO: OTA hostname sets to: "));
  DEBUG_PRINTLN(OTA_HOSTNAME);
#endif

#if defined(OTA_PORT)
  ArduinoOTA.setPort(OTA_PORT);
  DEBUG_PRINT(F("INFO: OTA port sets to: "));
  DEBUG_PRINTLN(OTA_PORT);
#endif

#if defined(OTA_PASSWORD)
  ArduinoOTA.setPassword((const char *)OTA_PASSWORD);
  DEBUG_PRINT(F("INFO: OTA password sets to: "));
  DEBUG_PRINTLN(OTA_PASSWORD);
#endif

  ArduinoOTA.onStart([]() {
    DEBUG_PRINTLN(F("INFO: OTA starts"));
  });
  ArduinoOTA.onEnd([]() {
    DEBUG_PRINTLN(F("INFO: OTA ends"));
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    DEBUG_PRINT(F("INFO: OTA progresses: "));
    DEBUG_PRINT(progress / (total / 100));
    DEBUG_PRINTLN(F("%"));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    DEBUG_PRINT(F("ERROR: OTA error: "));
    DEBUG_PRINTLN(error);
    if (error == OTA_AUTH_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA auth failed"));
    else if (error == OTA_BEGIN_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA begin failed"));
    else if (error == OTA_CONNECT_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA connect failed"));
    else if (error == OTA_RECEIVE_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA receive failed"));
    else if (error == OTA_END_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA end failed"));
  });
  ArduinoOTA.begin();
}

/*
   Function called to handle OTA updates
*/
void handleOTA() {
  ArduinoOTA.handle();
}
#endif



///////////////////////////////////////////////////////////////////////////
//   MQTT
///////////////////////////////////////////////////////////////////////////
volatile unsigned long lastMQTTConnection = 0;
char MQTT_CLIENT_ID[7] = {0};
char MQTT_PAYLOAD[8] = {0};
char MQTT_AVAILABILITY_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_AVAILABILITY_TOPIC_TEMPLATE) - 2] = {0};
#if defined(MOTION_SENSOR)
char MQTT_MOTION_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(MOTION_SENSOR_NAME) - 4] = {0};
#endif
#if defined(DOOR_SENSOR)
char MQTT_DOOR_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(DOOR_SENSOR_NAME) - 4] = {0};
#endif
#if defined(LDR_SENSOR)
char MQTT_LDR_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(LDR_SENSOR_NAME) - 4] = {0};
#endif
#if defined(DHT_SENSOR)
char MQTT_DHT_TEMPERATURE_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(DHT_TEMPERATURE_SENSOR_NAME) - 4] = {0};
char MQTT_DHT_HUMIDITY_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(DHT_HUMIDITY_SENSOR_NAME) - 4] = {0};
#endif
#if defined(SHT_SENSOR)
char MQTT_SHT_TEMPERATURE_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(SHT_TEMPERATURE_SENSOR_NAME) - 4] = {0};
char MQTT_SHT_HUMIDITY_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(SHT_HUMIDITY_SENSOR_NAME) - 4] = {0};
#endif
#if defined(BUTTON_SENSOR)
char MQTT_BUTTON_SENSOR_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_SENSOR_TOPIC_TEMPLATE) + sizeof(BUTTON_SENSOR_NAME) - 4] = {0};
#endif
/*
  Function called to publish to a MQTT topic with the given payload
*/
void publishToMQTT(char* p_topic, char* p_payload) {
  if (mqttClient.publish(p_topic, p_payload, true)) {
    DEBUG_PRINT(F("INFO: MQTT message published successfully, topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(", payload: "));
    DEBUG_PRINTLN(p_payload);
  } else {
    DEBUG_PRINTLN(F("ERROR: MQTT message not published, either connection lost, or message too large. Topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(" , payload: "));
    DEBUG_PRINTLN(p_payload);
  }
}

/*
  Function called to connect/reconnect to the MQTT broker
*/
void connectToMQTT() {
  if (!mqttClient.connected()) {
    if (lastMQTTConnection < millis()) {
      if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD, MQTT_AVAILABILITY_TOPIC, 0, 1, "offline")) {
        DEBUG_PRINTLN(F("INFO: The client is successfully connected to the MQTT broker"));
        publishToMQTT(MQTT_AVAILABILITY_TOPIC, "online");

        // publish sensors' states
#if defined(DOOR_SENSOR)
        if (!ms.getDoorState()) {
          publishToMQTT(MQTT_DOOR_SENSOR_TOPIC, MQTT_PAYLOAD_ON);
        } else {
          publishToMQTT(MQTT_DOOR_SENSOR_TOPIC, MQTT_PAYLOAD_OFF);
        }
#endif
#if defined(MOTION_SENSOR)
        if (ms.getMotionState()) {
          publishToMQTT(MQTT_MOTION_SENSOR_TOPIC, MQTT_PAYLOAD_ON);
        } else {
          publishToMQTT(MQTT_MOTION_SENSOR_TOPIC, MQTT_PAYLOAD_OFF);
        }
#endif
#if defined(LDR_SENSOR)
        itoa (ms.getLux(), MQTT_PAYLOAD, 10);
        publishToMQTT(MQTT_LDR_SENSOR_TOPIC, MQTT_PAYLOAD);
#endif
#if defined(DHT_SENSOR)
        dtostrf(ms.getDHTTemperature(), 2, 2, MQTT_PAYLOAD);
        publishToMQTT(MQTT_DHT_TEMPERATURE_SENSOR_TOPIC, MQTT_PAYLOAD);
        dtostrf(ms.getDHTHumidity(), 2, 2, MQTT_PAYLOAD);
        publishToMQTT(MQTT_DHT_HUMIDITY_SENSOR_TOPIC, MQTT_PAYLOAD);
#endif
#if defined(SHT_SENSOR)
        dtostrf(ms.getSHTTemperature(), 2, 2, MQTT_PAYLOAD);
        publishToMQTT(MQTT_SHT_TEMPERATURE_SENSOR_TOPIC, MQTT_PAYLOAD);
        dtostrf(ms.getSHTHumidity(), 2, 2, MQTT_PAYLOAD);
        publishToMQTT(MQTT_SHT_HUMIDITY_SENSOR_TOPIC, MQTT_PAYLOAD);
#endif
#if defined(BUTTON_SENSOR)
        if (ms.getButtonState()) {
          publishToMQTT(MQTT_BUTTON_SENSOR_TOPIC, MQTT_PAYLOAD_ON);
        } else {
          publishToMQTT(MQTT_BUTTON_SENSOR_TOPIC, MQTT_PAYLOAD_OFF);
        }
#endif
      } else {
        DEBUG_PRINTLN(F("ERROR: The connection to the MQTT broker failed"));
        DEBUG_PRINT(F("INFO: MQTT username: "));
        DEBUG_PRINTLN(MQTT_USERNAME);
        DEBUG_PRINT(F("INFO: MQTT password: "));
        DEBUG_PRINTLN(MQTT_PASSWORD);
        DEBUG_PRINT(F("INFO: MQTT broker: "));
        DEBUG_PRINTLN(MQTT_SERVER);
      }
      lastMQTTConnection = millis() + MQTT_CONNECTION_TIMEOUT;
    }
  }
}

void onMultiSensorEvent(uint8_t p_evt) {
  DEBUG_PRINT(F("INFO: onMultiSensorEvent(): evt: "));
  DEBUG_PRINTLN(p_evt);
  switch (p_evt) {
#if defined(DOOR_SENSOR)
    case DOOR_SENSOR_EVT:
      if (ms.getDoorState()) {
        // ON:  means that the door is opened (getDoorState() == 0)
        // OFF: means that the door is closed
        publishToMQTT(MQTT_DOOR_SENSOR_TOPIC, MQTT_PAYLOAD_OFF);
      } else {
        publishToMQTT(MQTT_DOOR_SENSOR_TOPIC, MQTT_PAYLOAD_ON);
      }
      break;
#endif
#if defined(MOTION_SENSOR)
    case MOTION_SENSOR_EVT:
      if (ms.getMotionState()) {
        publishToMQTT(MQTT_MOTION_SENSOR_TOPIC, MQTT_PAYLOAD_ON);
      } else {
        publishToMQTT(MQTT_MOTION_SENSOR_TOPIC, MQTT_PAYLOAD_OFF);
      }
      break;
#endif
#if defined(LDR_SENSOR)
    case LDR_SENSOR_EVT:
      itoa (ms.getLux(), MQTT_PAYLOAD, 10);
      publishToMQTT(MQTT_LDR_SENSOR_TOPIC, MQTT_PAYLOAD);
      break;
#endif
#if defined(DHT_SENSOR)
    case DHT_TEMPERATURE_SENSOR_EVT:
      dtostrf(ms.getDHTTemperature(), 2, 2, MQTT_PAYLOAD);
      publishToMQTT(MQTT_DHT_TEMPERATURE_SENSOR_TOPIC, MQTT_PAYLOAD);
      break;
    case DHT_HUMIDITY_SENSOR_EVT:
      dtostrf(ms.getDHTHumidity(), 2, 2, MQTT_PAYLOAD);
      publishToMQTT(MQTT_DHT_HUMIDITY_SENSOR_TOPIC, MQTT_PAYLOAD);
      break;
#endif
#if defined(SHT_SENSOR)
    case SHT_TEMPERATURE_SENSOR_EVT:
      dtostrf(ms.getSHTTemperature(), 2, 2, MQTT_PAYLOAD);
      publishToMQTT(MQTT_SHT_TEMPERATURE_SENSOR_TOPIC, MQTT_PAYLOAD);
      break;
    case SHT_HUMIDITY_SENSOR_EVT:
      dtostrf(ms.getSHTHumidity(), 2, 2, MQTT_PAYLOAD);
      publishToMQTT(MQTT_SHT_HUMIDITY_SENSOR_TOPIC, MQTT_PAYLOAD);
      break;
#endif
#if defined(BUTTON_SENSOR)
    case BUTTON_SENSOR_EVT:
      if (ms.getButtonState()) {
        publishToMQTT(MQTT_BUTTON_SENSOR_TOPIC, MQTT_PAYLOAD_ON);
      } else {
        publishToMQTT(MQTT_BUTTON_SENSOR_TOPIC, MQTT_PAYLOAD_OFF);
      }
      break;
#endif
  }
}

void setup() {
#if defined(DEBUG_SERIAL)
  Serial.begin(115200);
#endif

  setupWiFi();

#if defined(OTA)
  setupOTA();
#endif

  ms.init();
  ms.setCallback(onMultiSensorEvent);
  
  sprintf(MQTT_CLIENT_ID, "%06X", ESP.getChipId());
  sprintf(MQTT_AVAILABILITY_TOPIC, MQTT_AVAILABILITY_TOPIC_TEMPLATE, MQTT_CLIENT_ID);

  DEBUG_PRINT(F("INFO: MQTT availability topic: "));
  DEBUG_PRINTLN(MQTT_AVAILABILITY_TOPIC);
  
#if defined(MOTION_SENSOR)
  sprintf(MQTT_MOTION_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, MOTION_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT motion sensor topic: "));
  DEBUG_PRINTLN(MQTT_MOTION_SENSOR_TOPIC);
#endif
#if defined(DOOR_SENSOR)
  sprintf(MQTT_DOOR_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, DOOR_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT door sensor topic: "));
  DEBUG_PRINTLN(MQTT_DOOR_SENSOR_TOPIC);
#endif
#if defined(LDR_SENSOR)
  sprintf(MQTT_LDR_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, LDR_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT luminosity sensor topic: "));
  DEBUG_PRINTLN(MQTT_LDR_SENSOR_TOPIC);
#endif
#if defined(DHT_SENSOR)
  sprintf(MQTT_DHT_TEMPERATURE_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, DHT_TEMPERATURE_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT DHT temperature sensor topic: "));
  DEBUG_PRINTLN(MQTT_DHT_TEMPERATURE_SENSOR_TOPIC);
  sprintf(MQTT_DHT_HUMIDITY_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, DHT_HUMIDITY_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT DHT humidity sensor topic: "));
  DEBUG_PRINTLN(MQTT_DHT_HUMIDITY_SENSOR_TOPIC);
#endif
#if defined(SHT_SENSOR)
  sprintf(MQTT_SHT_TEMPERATURE_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, SHT_TEMPERATURE_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT SHT temperature sensor topic: "));
  DEBUG_PRINTLN(MQTT_SHT_TEMPERATURE_SENSOR_TOPIC);
  sprintf(MQTT_SHT_HUMIDITY_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, SHT_HUMIDITY_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT SHT humidity sensor topic: "));
  DEBUG_PRINTLN(MQTT_SHT_HUMIDITY_SENSOR_TOPIC);
#endif
#if defined(BUTTON_SENSOR)
  sprintf(MQTT_BUTTON_SENSOR_TOPIC, MQTT_SENSOR_TOPIC_TEMPLATE, MQTT_CLIENT_ID, BUTTON_SENSOR_NAME);
  DEBUG_PRINT(F("INFO: MQTT button sensor topic: "));
  DEBUG_PRINTLN(MQTT_BUTTON_SENSOR_TOPIC);
#endif

  mqttClient.setServer(MQTT_SERVER, MQTT_SERVER_PORT);

  connectToMQTT();
}

void loop() {
  connectToMQTT();
  mqttClient.loop();

  yield();
  
  ms.loop();

  yield();
  
#if defined(OTA)
  handleOTA();
  yield();
#endif
}


================================================
FILE: ha_mqtt_rgb_light/README.md
================================================
# MQTT RGB Light - Home Assistant
A simple example to control a RGB led connected to a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
light:
  platform: mqtt
  name: 'Office RGB light'
  state_topic: 'office/rgb1/light/status'
  command_topic: 'office/rgb1/light/switch'
  brightness_state_topic: 'office/rgb1/brightness/status'
  brightness_command_topic: 'office/rgb1/brightness/set'
  rgb_state_topic: 'office/rgb1/rgb/status'
  rgb_command_topic: 'office/rgb1/rgb/set'
  brightness_scale: 100
  optimistic: false
```

## Schematic
- LED leg 1 - Resistor 270 Ohms - D1/GPIO5
- LED leg 2 (longuest leg) - GND
- LED leg 3 - Resistor 270 Ohms - D2/GPIO4
- LED leg 4 - Resistor 270 Ohms - D3/GPIO0

![Schematic](Schematic.png)

## Demo
[![Home-Assistant + MQTT + RGB light](screenshot.png)](https://www.youtube.com/watch?v=7rMpCZ9I2BU "Home-Assistant + MQTT + RGB light")


================================================
FILE: ha_mqtt_rgb_light/ha_mqtt_rgb_light.ino
================================================
/*
   MQTT RGB Light for Home-Assistant - NodeMCU (ESP8266)
   https://home-assistant.io/components/light.mqtt/

   Libraries :
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient

   Sources :
    - File > Examples > ES8266WiFi > WiFiClient
    - File > Examples > PubSubClient > mqtt_auth
    - File > Examples > PubSubClient > mqtt_esp8266
    - http://forum.arduino.cc/index.php?topic=272862.0

   Schematic :
    - https://github.com/mertenats/open-home-automation/blob/master/ha_mqtt_rgb_light/Schematic.png
    - LED leg 1 - Resistor 270 Ohms - D1/GPIO5
    - LED leg 2 (longuest leg) - GND
    - LED leg 3 - Resistor 270 Ohms - D2/GPIO4
    - LED leg 4 - Resistor 270 Ohms - D3/GPIO0

   Configuration (HA) : 
    light:
      platform: mqtt
      name: 'Office RGB light'
      state_topic: 'office/rgb1/light/status'
      command_topic: 'office/rgb1/light/switch'
      brightness_state_topic: 'office/rgb1/brightness/status'
      brightness_command_topic: 'office/rgb1/brightness/set'
      rgb_state_topic: 'office/rgb1/rgb/status'
      rgb_command_topic: 'office/rgb1/rgb/set'
      brightness_scale: 100
      optimistic: false

   Samuel M. - v1.1 - 08.2016
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define MQTT_VERSION MQTT_VERSION_3_1_1

// Wifi: SSID and password
const char* WIFI_SSID = "[Redacted]";
const char* WIFI_PASSWORD = "[Redacted]";

// MQTT: ID, server IP, port, username and password
const PROGMEM char* MQTT_CLIENT_ID = "office_rgb_light";
const PROGMEM char* MQTT_SERVER_IP = "[Redacted]";
const PROGMEM uint16_t MQTT_SERVER_PORT = 1883;
const PROGMEM char* MQTT_USER = "[Redacted]";
const PROGMEM char* MQTT_PASSWORD = "[Redacted]";

// MQTT: topics
// state
const PROGMEM char* MQTT_LIGHT_STATE_TOPIC = "office/rgb1/light/status";
const PROGMEM char* MQTT_LIGHT_COMMAND_TOPIC = "office/rgb1/light/switch";

// brightness
const PROGMEM char* MQTT_LIGHT_BRIGHTNESS_STATE_TOPIC = "office/rgb1/brightness/status";
const PROGMEM char* MQTT_LIGHT_BRIGHTNESS_COMMAND_TOPIC = "office/rgb1/brightness/set";

// colors (rgb)
const PROGMEM char* MQTT_LIGHT_RGB_STATE_TOPIC = "office/rgb1/rgb/status";
const PROGMEM char* MQTT_LIGHT_RGB_COMMAND_TOPIC = "office/rgb1/rgb/set";

// payloads by default (on/off)
const PROGMEM char* LIGHT_ON = "ON";
const PROGMEM char* LIGHT_OFF = "OFF";

// variables used to store the state, the brightness and the color of the light
boolean m_rgb_state = false;
uint8_t m_rgb_brightness = 100;
uint8_t m_rgb_red = 255;
uint8_t m_rgb_green = 255;
uint8_t m_rgb_blue = 255;

// pins used for the rgb led (PWM)
const PROGMEM uint8_t RGB_LIGHT_RED_PIN = 5;
const PROGMEM uint8_t RGB_LIGHT_GREEN_PIN = 0;
const PROGMEM uint8_t RGB_LIGHT_BLUE_PIN = 4;

// buffer used to send/receive data with MQTT
const uint8_t MSG_BUFFER_SIZE = 20;
char m_msg_buffer[MSG_BUFFER_SIZE]; 

WiFiClient wifiClient;
PubSubClient client(wifiClient);

// function called to adapt the brightness and the color of the led
void setColor(uint8_t p_red, uint8_t p_green, uint8_t p_blue) {
  // convert the brightness in % (0-100%) into a digital value (0-255)
  uint8_t brightness = map(m_rgb_brightness, 0, 100, 0, 255);

  analogWrite(RGB_LIGHT_RED_PIN, map(p_red, 0, 255, 0, brightness));
  analogWrite(RGB_LIGHT_GREEN_PIN, map(p_green, 0, 255, 0, brightness));
  analogWrite(RGB_LIGHT_BLUE_PIN, map(p_blue, 0, 255, 0, brightness));
}

// function called to publish the state of the led (on/off)
void publishRGBState() {
  if (m_rgb_state) {
    client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_ON, true);
  } else {
    client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_OFF, true);
  }
}

// function called to publish the brightness of the led (0-100)
void publishRGBBrightness() {
  snprintf(m_msg_buffer, MSG_BUFFER_SIZE, "%d", m_rgb_brightness);
  client.publish(MQTT_LIGHT_BRIGHTNESS_STATE_TOPIC, m_msg_buffer, true);
}

// function called to publish the colors of the led (xx(x),xx(x),xx(x))
void publishRGBColor() {
  snprintf(m_msg_buffer, MSG_BUFFER_SIZE, "%d,%d,%d", m_rgb_red, m_rgb_green, m_rgb_blue);
  client.publish(MQTT_LIGHT_RGB_STATE_TOPIC, m_msg_buffer, true);
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
  // concat the payload into a string
  String payload;
  for (uint8_t i = 0; i < p_length; i++) {
    payload.concat((char)p_payload[i]);
  }
  // handle message topic
  if (String(MQTT_LIGHT_COMMAND_TOPIC).equals(p_topic)) {
    // test if the payload is equal to "ON" or "OFF"
    if (payload.equals(String(LIGHT_ON))) {
      if (m_rgb_state != true) {
        m_rgb_state = true;
        setColor(m_rgb_red, m_rgb_green, m_rgb_blue);
        publishRGBState();
      }
    } else if (payload.equals(String(LIGHT_OFF))) {
      if (m_rgb_state != false) {
        m_rgb_state = false;
        setColor(0, 0, 0);
        publishRGBState();
      }
    }
  } else if (String(MQTT_LIGHT_BRIGHTNESS_COMMAND_TOPIC).equals(p_topic)) {
    uint8_t brightness = payload.toInt();
    if (brightness < 0 || brightness > 100) {
      // do nothing...
      return;
    } else {
      m_rgb_brightness = brightness;
      setColor(m_rgb_red, m_rgb_green, m_rgb_blue);
      publishRGBBrightness();
    }
  } else if (String(MQTT_LIGHT_RGB_COMMAND_TOPIC).equals(p_topic)) {
    // get the position of the first and second commas
    uint8_t firstIndex = payload.indexOf(',');
    uint8_t lastIndex = payload.lastIndexOf(',');
    
    uint8_t rgb_red = payload.substring(0, firstIndex).toInt();
    if (rgb_red < 0 || rgb_red > 255) {
      return;
    } else {
      m_rgb_red = rgb_red;
    }
    
    uint8_t rgb_green = payload.substring(firstIndex + 1, lastIndex).toInt();
    if (rgb_green < 0 || rgb_green > 255) {
      return;
    } else {
      m_rgb_green = rgb_green;
    }
    
    uint8_t rgb_blue = payload.substring(lastIndex + 1).toInt();
    if (rgb_blue < 0 || rgb_blue > 255) {
      return;
    } else {
      m_rgb_blue = rgb_blue;
    }
    
    setColor(m_rgb_red, m_rgb_green, m_rgb_blue);
    publishRGBColor();
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
      Serial.println("INFO: connected");
      
      // Once connected, publish an announcement...
      // publish the initial values
      publishRGBState();
      publishRGBBrightness();
      publishRGBColor();

      // ... and resubscribe
      client.subscribe(MQTT_LIGHT_COMMAND_TOPIC);
      client.subscribe(MQTT_LIGHT_BRIGHTNESS_COMMAND_TOPIC);
      client.subscribe(MQTT_LIGHT_RGB_COMMAND_TOPIC);
    } else {
      Serial.print("ERROR: failed, rc=");
      Serial.print(client.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  // init the serial
  Serial.begin(115200);

  // init the RGB led
  pinMode(RGB_LIGHT_BLUE_PIN, OUTPUT);
  pinMode(RGB_LIGHT_RED_PIN, OUTPUT);
  pinMode(RGB_LIGHT_GREEN_PIN, OUTPUT);
  analogWriteRange(255);
  setColor(0, 0, 0);

  // init the WiFi connection
  Serial.println();
  Serial.println();
  Serial.print("INFO: Connecting to ");
  WiFi.mode(WIFI_STA);
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("INFO: WiFi connected");
  Serial.print("INFO: IP address: ");
  Serial.println(WiFi.localIP());

  // init the MQTT connection
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}


================================================
FILE: ha_mqtt_rgbw_light_with_discovery/README.md
================================================
# MQTT JSON Light - Home Assistant
A simple example to control a RGB led and a white led connected to a NodeMCU board (ESP8266).

## Features
- MQTT discovery
- MQTT JSON Light
  - State
  - RGB
  - White
  - Brightness
  - Color temperature
  - Effect
    - Rainbow

## How to
1. Rename `config.example.h`to `config.h`
2. Define your WiFi SSID and password
3. Define your MQTT settings
4. Install the external libraries (PubSubClient, ArduinoJson)
4. Define `MQTT_MAX_PACKET_SIZE` to `256`in `Arduino/libraries/pubsubclient/src/PubSubClient.h`


================================================
FILE: ha_mqtt_rgbw_light_with_discovery/config.example.h
================================================
///////////////////////////////////////////////////////////////////////////
//   RGB PINS
///////////////////////////////////////////////////////////////////////////
#define RED_PIN   D1
#define GREEN_PIN D2
#define BLUE_PIN  D3
#define WHITE_PIN D4

///////////////////////////////////////////////////////////////////////////
//   MISC
///////////////////////////////////////////////////////////////////////////
#define FRIENDLY_NAME "MQTT RGBW Light"

///////////////////////////////////////////////////////////////////////////
//   WiFi
///////////////////////////////////////////////////////////////////////////
#define WIFI_SSID     ""
#define WIFI_PASSWORD ""

///////////////////////////////////////////////////////////////////////////
//   MQTT
///////////////////////////////////////////////////////////////////////////
#define MQTT_USERNAME     ""
#define MQTT_PASSWORD     ""
#define MQTT_SERVER       ""
#define MQTT_SERVER_PORT  1883

#define MQTT_HOME_ASSISTANT_SUPPORT

#if defined(MQTT_HOME_ASSISTANT_SUPPORT)
// template: <discovery prefix>/light/<chip ID>/config, status, state or set
#define MQTT_CONFIG_TOPIC_TEMPLATE  "%s/light/%s/config"
#else

#endif

#define MQTT_STATE_TOPIC_TEMPLATE   "%s/rgbw/state"
#define MQTT_COMMAND_TOPIC_TEMPLATE "%s/rgbw/set"
#define MQTT_STATUS_TOPIC_TEMPLATE  "%s/rgbw/status" // MQTT connection: alive/dead

#define MQTT_HOME_ASSISTANT_DISCOVERY_PREFIX  "homeassistant"
#define MQTT_STATE_ON_PAYLOAD   "ON"
#define MQTT_STATE_OFF_PAYLOAD  "OFF"

#define MQTT_CONNECTION_TIMEOUT 5000

///////////////////////////////////////////////////////////////////////////
//   DEBUG
///////////////////////////////////////////////////////////////////////////
//#define DEBUG_TELNET
//#define DEBUG_SERIAL

///////////////////////////////////////////////////////////////////////////
//   OTA
///////////////////////////////////////////////////////////////////////////
#define OTA
//#define OTA_HOSTNAME  "hostname"  // hostname esp8266-[ChipID] by default
//#define OTA_PASSWORD  "password"  // no password by default
//#define OTA_PORT      8266        // port 8266 by default


================================================
FILE: ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.cpp
================================================
#include "ha_mqtt_rgbw_light_with_discovery.h"

volatile uint8_t effect = EFFECT_NOT_DEFINED;

///////////////////////////////////////////////////////////////////////////
//   CONSTRUCTOR, INIT() AND LOOP()
///////////////////////////////////////////////////////////////////////////
AIRGBWBulb::AIRGBWBulb(void) {
  analogWriteRange(255);

  pinMode(RED_PIN, OUTPUT);  
  pinMode(GREEN_PIN, OUTPUT);  
  pinMode(BLUE_PIN, OUTPUT);  
  pinMode(WHITE_PIN, OUTPUT);  
}

void AIRGBWBulb::init(void) {
  m_state = false;
  m_brightness = 255;
  m_color.red = 0;
  m_color.green = 0;
  m_color.blue = 0;
  m_color.white = 255;
}

void AIRGBWBulb::loop(void) {
  // TODO: handles effects
  switch(effect) {
    case EFFECT_NOT_DEFINED:
      break;
    case EFFECT_RAMBOW:
      static unsigned char count = 0;
      static unsigned long last = millis();
      if (millis() - last > EFFECT_RAINBOW_DELAY) {
        last = millis();
        rainbowEffect(count++);
      }
      break;
  }
}

///////////////////////////////////////////////////////////////////////////
//   STATE
///////////////////////////////////////////////////////////////////////////
bool AIRGBWBulb::getState(void) {
  return m_state;
}

bool AIRGBWBulb::setState(bool p_state) {
  // checks if the given state is different from the actual state
  if (p_state == m_state)
    return false;
  
  if (p_state) {
    m_state = true;
    setColor();
  } else {
    m_state = false;
    analogWrite(RED_PIN, 0);
    analogWrite(GREEN_PIN, 0);
    analogWrite(BLUE_PIN, 0);
    analogWrite(WHITE_PIN, 0);
  } 

  return true;
}

///////////////////////////////////////////////////////////////////////////
//   BRIGHTNESS
///////////////////////////////////////////////////////////////////////////
uint8_t AIRGBWBulb::getBrightness(void) {
  return m_brightness;
}

bool AIRGBWBulb::setBrightness(uint8_t p_brightness) {
  // checks if the value is smaller, bigger or equal to the actual brightness value
  if (p_brightness < 0 || p_brightness > 255 || p_brightness == m_brightness)
    return false;

  // saves the new brightness value
  m_brightness = p_brightness;

  if (m_color.white != 0)
    m_color.white = p_brightness;

  return setColor();
}

///////////////////////////////////////////////////////////////////////////
//   RGB COLOR
///////////////////////////////////////////////////////////////////////////
Color AIRGBWBulb::getColor(void) {
  return m_color;
}

bool AIRGBWBulb::setColor(uint8_t p_red, uint8_t p_green, uint8_t p_blue) {
  if ((p_red < 0 || p_red > 255) || (p_green < 0 || p_green > 255) || (p_blue < 0 || p_blue > 255))
    return false;

  // saves the new values
  m_color.red = p_red;
  m_color.green = p_green;
  m_color.blue = p_blue;

  // switches off the white leds
  m_color.white = 0;

  return setColor();
}

bool AIRGBWBulb::setColor() {
  // sets the new color
  analogWrite(RED_PIN, map(m_color.red, 0, 255, 0, m_brightness));
  analogWrite(GREEN_PIN, map(m_color.green, 0, 255, 0, m_brightness));
  analogWrite(BLUE_PIN, map(m_color.blue, 0, 255, 0, m_brightness));
  analogWrite(WHITE_PIN, m_color.white);

  return true;
}

///////////////////////////////////////////////////////////////////////////
//   WHITE COLOR
///////////////////////////////////////////////////////////////////////////
bool AIRGBWBulb::setWhite(uint8_t p_white) {
  // checks if the value is smaller, bigger or equal to the actual white value
  if (p_white < 0 || p_white > 255 || p_white == m_color.white)
    return false;

  // saves the new white value
  m_color.white = p_white;
  m_brightness = p_white;

  // switch off the RGB leds
  m_color.red = 0;
  m_color.green = 0;
  m_color.blue = 0;

  // adjusts the white value
  analogWrite(RED_PIN, m_color.red);
  analogWrite(GREEN_PIN, m_color.green);
  analogWrite(BLUE_PIN, m_color.blue);
  analogWrite(WHITE_PIN, m_color.white);

  return true;
}

///////////////////////////////////////////////////////////////////////////
//   COLOR TEMPERATURE
///////////////////////////////////////////////////////////////////////////
uint16_t AIRGBWBulb::getColorTemperature(void) {
  return m_colorTemperature;
}

bool AIRGBWBulb::setColorTemperature(uint16_t p_colorTemperature) {
  // checks if the value is equal to the actual color temperature
  if (p_colorTemperature < COLOR_TEMP_HA_MIN_IN_MIRED || p_colorTemperature == m_colorTemperature || p_colorTemperature > COLOR_TEMP_HA_MAX_IN_MIRED)
    return false;

  // switches off the white leds
  m_color.white = 0;

  // saves the new colour temperature
  m_colorTemperature = p_colorTemperature;

  // https://fr.wikipedia.org/wiki/Mired
  // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
  // M = 1000000 / T <> T [kelvin] = 1000000 / M [mired]
      int tmpKelvin = 1000000 / m_colorTemperature;
  
      if (tmpKelvin < 1000) {
        tmpKelvin = 1000;
      } else if (tmpKelvin > 40000) {
        tmpKelvin = 40000;
      }

  //int tmpKelvin = map(p_colorTemperature, COLOR_TEMP_HA_MIN_IN_MIRED, COLOR_TEMP_HA_MAX_IN_MIRED, COLOR_TEMP_MAX_IN_KELVIN, COLOR_TEMP_MIN_IN_KELVIN);
  tmpKelvin = tmpKelvin / 100;

  // computes red
  if (tmpKelvin <= 66) {
    m_color.red = 255;
  } else {
    float red = tmpKelvin - 60;
    red = 329.698727446 * pow(red, -0.1332047592);
    if (red < 0) {
      m_color.red = 0;
    } else if (red > 255) {
      m_color.red = 255;
    } else {
      m_color.red = red;
    }
  }

  // computes green
  if (tmpKelvin <= 66) {
    float green = tmpKelvin;
    green = 99.4708025861 * log(green) - 161.1195681661;
    if (green < 0) {
      m_color.green = 0;
    } else if (green > 255) {
      m_color.green = 255;
    } else {
      m_color.green = green;
    }
  } else {
    float green = tmpKelvin - 60;
    green = 288.1221695283 * pow(green, -0.0755148492);
    if (green < 0) {
      m_color.green = 0;
    } else if (green > 255) {
      m_color.green = 255;
    } else {
      m_color.green = green;
    }
  }

  // computes blue
  if (tmpKelvin <= 66) {
    m_color.blue = 255;
  } else {
    if (tmpKelvin <= 19) {
      m_color.blue = 0;
    } else {
      float blue = tmpKelvin - 10;
      blue = 138.5177312231 * log(blue) - 305.0447927307;
      if (blue < 0) {
        m_color.blue = 0;
      } else if (blue > 255) {
        m_color.blue = 255;
      } else {
        m_color.blue = blue;
      }
    }
  }
  
  return setColor();
}

///////////////////////////////////////////////////////////////////////////
//   EFFECTS
///////////////////////////////////////////////////////////////////////////
bool AIRGBWBulb::setEffect(const char* p_effect) {
  if (strcmp(p_effect, EFFECT_NOT_DEFINED_NAME) == 0) {
    effect = EFFECT_NOT_DEFINED;
    return true;
  } else if (strcmp(p_effect, EFFECT_RAMBOW_NAME) == 0) {
    effect = EFFECT_RAMBOW;
    return true;
  }

  return false;
}

// https://github.com/xoseperez/my9291/blob/master/examples/esp8285/esp8285_rainbow.cpp
void AIRGBWBulb::rainbowEffect(uint8_t p_index) {
  if (p_index < 85) {
    setColor(p_index * 3, 255 - p_index * 3, 0);
  } else if (p_index < 170) {
    p_index -= 85;
    setColor(255 - p_index * 3, 0, p_index * 3);
  } else {
    p_index -= 170;
    setColor(0, p_index * 3, 255 - p_index * 3);
  }
}


================================================
FILE: ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.h
================================================
#pragma once
#ifndef _RGBW_
#define _RGBW_

#include <ESP8266WiFi.h>          // https://github.com/esp8266/Arduino
#include "config.h"

#define COLOR_TEMP_HA_MIN_IN_MIRED   154    // Home Assistant minimum color temperature
#define COLOR_TEMP_HA_MAX_IN_MIRED   500    // Home Assistant maximum color temperature
#define COLOR_TEMP_MIN_IN_KELVIN     1000   // minimum color temperature
#define COLOR_TEMP_MAX_IN_KELVIN     15000  // maximum color temperature

typedef struct Color {
  uint8_t red;
  uint8_t green;
  uint8_t blue;
  uint8_t white;
};

enum CMD {
  CMD_NOT_DEFINED,
  CMD_STATE_CHANGED,
};

#define EFFECT_NOT_DEFINED_NAME "None"
#define EFFECT_RAMBOW_NAME      "Rainbow"
#define EFFECT_RAINBOW_DELAY    10

#define EFFECT_LIST EFFECT_RAMBOW_NAME

enum EFFECT {
  EFFECT_NOT_DEFINED,
  EFFECT_RAMBOW,
};

class AIRGBWBulb {
  public:
    AIRGBWBulb(void);
    
    void      init(void);
    void      loop(void);
    
    bool      getState(void);
    bool      setState(bool p_state);
    
    uint8_t   getBrightness(void);
    bool      setBrightness(uint8_t p_brightness);
    
    Color     getColor(void);
    bool      setColor(uint8_t p_red, uint8_t p_green, uint8_t p_blue);
    
    bool      setWhite(uint8_t p_white);
    
    uint16_t  getColorTemperature(void);
    bool      setColorTemperature(uint16_t p_colorTemperature);
    
    bool      setEffect(const char* p_effect);

  private:
    bool      m_state;    
    uint8_t   m_brightness;
    Color     m_color;
    uint16_t  m_colorTemperature;
    
    bool      setColor();

    void      rainbowEffect(uint8_t p_index);

};

#endif


================================================
FILE: ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.ino
================================================
/*
  MQTT Discovery and MQTT JSON Light for Home Assistant
  
  Samuel Mertenat
  04.2017
*/

#include <ESP8266WiFi.h>          // https://github.com/esp8266/Arduino
#include <PubSubClient.h>         // https://github.com/knolleary/pubsubclient
#include <ArduinoJson.h>          // https://github.com/bblanchon/ArduinoJson
#include <ArduinoOTA.h>
#include "ha_mqtt_rgbw_light_with_discovery.h"

#if defined(DEBUG_TELNET)
WiFiServer  telnetServer(23);
WiFiClient  telnetClient;
#define     DEBUG_PRINT(x)    telnetClient.print(x)
#define     DEBUG_PRINTLN(x)  telnetClient.println(x)
#elif defined(DEBUG_SERIAL)
#define     DEBUG_PRINT(x)    Serial.print(x)
#define     DEBUG_PRINTLN(x)  Serial.println(x)
#else
#define     DEBUG_PRINT(x)
#define     DEBUG_PRINTLN(x)
#endif

#if defined(MQTT_HOME_ASSISTANT_SUPPORT)
StaticJsonBuffer<256> staticJsonBuffer;
char jsonBuffer[256] = {0};
#endif

volatile uint8_t cmd = CMD_NOT_DEFINED;

AIRGBWBulb    bulb;
WiFiClient    wifiClient;
PubSubClient  mqttClient(wifiClient);

///////////////////////////////////////////////////////////////////////////
//   TELNET
///////////////////////////////////////////////////////////////////////////
/*
   Function called to handle Telnet clients
   https://www.youtube.com/watch?v=j9yW10OcahI
*/
#if defined(DEBUG_TELNET)
void handleTelnet(void) {
  if (telnetServer.hasClient()) {
    if (!telnetClient || !telnetClient.connected()) {
      if (telnetClient) {
        telnetClient.stop();
      }
      telnetClient = telnetServer.available();
    } else {
      telnetServer.available().stop();
    }
  }
}
#endif

///////////////////////////////////////////////////////////////////////////
//   WiFi
///////////////////////////////////////////////////////////////////////////
/*
   Function called to handle WiFi events
*/
void handleWiFiEvent(WiFiEvent_t event) {
  switch (event) {
    case WIFI_EVENT_STAMODE_GOT_IP:
      DEBUG_PRINTLN(F("INFO: WiFi connected"));
      DEBUG_PRINT(F("INFO: IP address: "));
      DEBUG_PRINTLN(WiFi.localIP());
      break;
    case WIFI_EVENT_STAMODE_DISCONNECTED:
      DEBUG_PRINTLN(F("ERROR: WiFi losts connection"));
      /*
         TODO: Do something smarter than rebooting the device
      */
      delay(5000);
      ESP.restart();
      break;
    default:
      DEBUG_PRINT(F("INFO: WiFi event: "));
      DEBUG_PRINTLN(event);
      break;
  }
}

/*
   Function called to setup the connection to the WiFi AP
*/
void setupWiFi() {
  DEBUG_PRINT(F("INFO: WiFi connecting to: "));
  DEBUG_PRINTLN(WIFI_SSID);

  delay(10);

  WiFi.mode(WIFI_STA);
  WiFi.onEvent(handleWiFiEvent);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  randomSeed(micros());
}

///////////////////////////////////////////////////////////////////////////
//   OTA
///////////////////////////////////////////////////////////////////////////
#if defined(OTA)
/*
   Function called to setup OTA updates
*/
void setupOTA() {
#if defined(OTA_HOSTNAME)
  ArduinoOTA.setHostname(OTA_HOSTNAME);
  DEBUG_PRINT(F("INFO: OTA hostname sets to: "));
  DEBUG_PRINTLN(OTA_HOSTNAME);
#endif

#if defined(OTA_PORT)
  ArduinoOTA.setPort(OTA_PORT);
  DEBUG_PRINT(F("INFO: OTA port sets to: "));
  DEBUG_PRINTLN(OTA_PORT);
#endif

#if defined(OTA_PASSWORD)
  ArduinoOTA.setPassword((const char *)OTA_PASSWORD);
  DEBUG_PRINT(F("INFO: OTA password sets to: "));
  DEBUG_PRINTLN(OTA_PASSWORD);
#endif

  ArduinoOTA.onStart([]() {
    DEBUG_PRINTLN(F("INFO: OTA starts"));
  });
  ArduinoOTA.onEnd([]() {
    DEBUG_PRINTLN(F("INFO: OTA ends"));
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    DEBUG_PRINT(F("INFO: OTA progresses: "));
    DEBUG_PRINT(progress / (total / 100));
    DEBUG_PRINTLN(F("%"));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    DEBUG_PRINT(F("ERROR: OTA error: "));
    DEBUG_PRINTLN(error);
    if (error == OTA_AUTH_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA auth failed"));
    else if (error == OTA_BEGIN_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA begin failed"));
    else if (error == OTA_CONNECT_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA connect failed"));
    else if (error == OTA_RECEIVE_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA receive failed"));
    else if (error == OTA_END_ERROR)
      DEBUG_PRINTLN(F("ERROR: OTA end failed"));
  });
  ArduinoOTA.begin();
}

/*
   Function called to handle OTA updates
*/
void handleOTA() {
  ArduinoOTA.handle();
}
#endif

///////////////////////////////////////////////////////////////////////////
//   MQTT
///////////////////////////////////////////////////////////////////////////

char MQTT_CLIENT_ID[7] = {0};
#if defined(MQTT_HOME_ASSISTANT_SUPPORT)
char MQTT_CONFIG_TOPIC[sizeof(MQTT_HOME_ASSISTANT_DISCOVERY_PREFIX) + sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_CONFIG_TOPIC_TEMPLATE) - 4] = {0};
#else

#endif

char MQTT_STATE_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_STATE_TOPIC_TEMPLATE) - 2] = {0};
char MQTT_COMMAND_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_COMMAND_TOPIC_TEMPLATE) - 2] = {0};
char MQTT_STATUS_TOPIC[sizeof(MQTT_CLIENT_ID) + sizeof(MQTT_STATUS_TOPIC_TEMPLATE) - 2] = {0};

volatile unsigned long lastMQTTConnection = MQTT_CONNECTION_TIMEOUT;
/*
   Function called when a MQTT message has arrived
   @param p_topic   The topic of the MQTT message
   @param p_payload The payload of the MQTT message
   @param p_length  The length of the payload
*/
void handleMQTTMessage(char* p_topic, byte* p_payload, unsigned int p_length) {
  // concatenates the payload into a string
  String payload;
  for (uint8_t i = 0; i < p_length; i++) {
    payload.concat((char)p_payload[i]);
  }

  DEBUG_PRINTLN(F("INFO: New MQTT message received"));
  DEBUG_PRINT(F("INFO: MQTT topic: "));
  DEBUG_PRINTLN(p_topic);
  DEBUG_PRINT(F("INFO: MQTT payload: "));
  DEBUG_PRINTLN(payload);
  
  if (String(MQTT_COMMAND_TOPIC).equals(p_topic)) {
    DynamicJsonBuffer dynamicJsonBuffer;
    JsonObject& root = dynamicJsonBuffer.parseObject(p_payload);
    if (!root.success()) {
      DEBUG_PRINTLN(F("ERROR: parseObject() failed"));
      return;
    }

    if (root.containsKey("state")) {
      if (strcmp(root["state"], MQTT_STATE_ON_PAYLOAD) == 0) {
        if (bulb.setState(true)) {
          DEBUG_PRINT(F("INFO: State changed to: "));
          DEBUG_PRINTLN(bulb.getState());
          cmd = CMD_STATE_CHANGED;
        }
      } else if (strcmp(root["state"], MQTT_STATE_OFF_PAYLOAD) == 0) {
        // stops the possible current effect
        bulb.setEffect(EFFECT_NOT_DEFINED_NAME);
        
        if (bulb.setState(false)) {
          DEBUG_PRINT(F("INFO: State changed to: "));
          DEBUG_PRINTLN(bulb.getState());
          cmd = CMD_STATE_CHANGED;
        }
      }
    }
    
    if (root.containsKey("color")) {
      // stops the possible current effect
      bulb.setEffect(EFFECT_NOT_DEFINED_NAME);
      
      uint8_t r = root["color"]["r"];
      uint8_t g = root["color"]["g"];
      uint8_t b = root["color"]["b"];

      if (bulb.setColor(r, g, b)) {
        DEBUG_PRINT(F("INFO: Color changed to: "));
        DEBUG_PRINT(bulb.getColor().red);
        DEBUG_PRINT(F(", "));
        DEBUG_PRINT(bulb.getColor().green);
        DEBUG_PRINT(F(", "));
        DEBUG_PRINTLN(bulb.getColor().blue);
        cmd = CMD_STATE_CHANGED;
      }
    }

    if (root.containsKey("brightness")) {
      if (bulb.setBrightness(root["brightness"])) {
        DEBUG_PRINT(F("INFO: Brightness changed to: "));
        DEBUG_PRINTLN(bulb.getBrightness());
        cmd = CMD_STATE_CHANGED;
      }
    }

    if (root.containsKey("white_value")) {
      // stops the possible current effect
      bulb.setEffect(EFFECT_NOT_DEFINED_NAME);
      
      if (bulb.setWhite(root["white_value"])) {
        DEBUG_PRINT(F("INFO: White changed to: "));
        DEBUG_PRINTLN(bulb.getColor().white);
        cmd = CMD_STATE_CHANGED;
      }
    }

    if (root.containsKey("color_temp")) {
      // stops the possible current effect
      bulb.setEffect(EFFECT_NOT_DEFINED_NAME);
      
      if (bulb.setColorTemperature(root["color_temp"])) {
        DEBUG_PRINT(F("INFO: Color temperature changed to: "));
        DEBUG_PRINTLN(bulb.getColorTemperature());
        cmd = CMD_STATE_CHANGED;
      }
    }
    
    if (root.containsKey("effect")) {
      const char* effect = root["effect"];
      if (bulb.setEffect(effect)) {
        DEBUG_PRINTLN(F("INFO: Effect started"));
        cmd = CMD_NOT_DEFINED;
      }
    }
  }
}

/*
  Function called to subscribe to a MQTT topic
*/
void subscribeToMQTT(char* p_topic) {
  if (mqttClient.subscribe(p_topic)) {
    DEBUG_PRINT(F("INFO: Sending the MQTT subscribe succeeded for topic: "));
    DEBUG_PRINTLN(p_topic);
  } else {
    DEBUG_PRINT(F("ERROR: Sending the MQTT subscribe failed for topic: "));
    DEBUG_PRINTLN(p_topic);
  }
}

/*
  Function called to publish to a MQTT topic with the given payload
*/
void publishToMQTT(char* p_topic, char* p_payload) {
  if (mqttClient.publish(p_topic, p_payload, true)) {
    DEBUG_PRINT(F("INFO: MQTT message published successfully, topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(", payload: "));
    DEBUG_PRINTLN(p_payload);
  } else {
    DEBUG_PRINTLN(F("ERROR: MQTT message not published, either connection lost, or message too large. Topic: "));
    DEBUG_PRINT(p_topic);
    DEBUG_PRINT(F(" , payload: "));
    DEBUG_PRINTLN(p_payload);
  }
}

/*
  Function called to connect/reconnect to the MQTT broker
*/
void connectToMQTT() {
  if (!mqttClient.connected()) {
    if (lastMQTTConnection + MQTT_CONNECTION_TIMEOUT < millis()) {
      if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD, MQTT_STATUS_TOPIC, 0, 1, "dead")) {
        DEBUG_PRINTLN(F("INFO: The client is successfully connected to the MQTT broker"));
        publishToMQTT(MQTT_STATUS_TOPIC, "alive");

#if defined(MQTT_HOME_ASSISTANT_SUPPORT)
        // MQTT discovery for Home Assistant
        JsonObject& root = staticJsonBuffer.createObject();
        root["name"] = FRIENDLY_NAME;
        root["platform"] = "mqtt_json";
        root["state_topic"] = MQTT_STATE_TOPIC;
        root["command_topic"] = MQTT_COMMAND_TOPIC;
        root["brightness"] = true;
        root["rgb"] = true;
        root["white_value"] = true;
        root["color_temp"] = true;
        root["effect"] = true;
        root["effect_list"] = EFFECT_LIST;
        root.printTo(jsonBuffer, sizeof(jsonBuffer));
        publishToMQTT(MQTT_CONFIG_TOPIC, jsonBuffer);
#endif

        subscribeToMQTT(MQTT_COMMAND_TOPIC);
      } else {
        DEBUG_PRINTLN(F("ERROR: The connection to the MQTT broker failed"));
        DEBUG_PRINT(F("INFO: MQTT username: "));
        DEBUG_PRINTLN(MQTT_USERNAME);
        DEBUG_PRINT(F("INFO: MQTT password: "));
        DEBUG_PRINTLN(MQTT_PASSWORD);
        DEBUG_PRINT(F("INFO: MQTT broker: "));
        DEBUG_PRINTLN(MQTT_SERVER);
      }
        lastMQTTConnection = millis();
    }
  }
}

///////////////////////////////////////////////////////////////////////////
//  CMD
///////////////////////////////////////////////////////////////////////////

void handleCMD() {
  switch(cmd) {
    case CMD_NOT_DEFINED:
      break;
    case CMD_STATE_CHANGED:
      cmd = CMD_NOT_DEFINED;
      DynamicJsonBuffer dynamicJsonBuffer;
      JsonObject& root = dynamicJsonBuffer.createObject();
      root["state"] = bulb.getState() ? MQTT_STATE_ON_PAYLOAD : MQTT_STATE_OFF_PAYLOAD;
      root["brightness"] = bulb.getBrightness();
      JsonObject& color = root.createNestedObject("color");
      color["r"] = bulb.getColor().red;
      color["g"] = bulb.getColor().green;
      color["b"] = bulb.getColor().blue;
      root["white_value"] = bulb.getColor().white;
      root["color_temp"] = bulb.getColorTemperature();
      root.printTo(jsonBuffer, sizeof(jsonBuffer));
      publishToMQTT(MQTT_STATE_TOPIC, jsonBuffer);
      break;
  }
}

///////////////////////////////////////////////////////////////////////////
//  SETUP() AND LOOP()
///////////////////////////////////////////////////////////////////////////

void setup() {
#if defined(DEBUG_SERIAL)
  Serial.begin(115200);
#elif defined(DEBUG_TELNET)
  telnetServer.begin();
  telnetServer.setNoDelay(true);
#endif

  setupWiFi();

  sprintf(MQTT_CLIENT_ID, "%06X", ESP.getChipId());
#if defined(MQTT_HOME_ASSISTANT_SUPPORT)
  sprintf(MQTT_CONFIG_TOPIC, MQTT_CONFIG_TOPIC_TEMPLATE, MQTT_HOME_ASSISTANT_DISCOVERY_PREFIX, MQTT_CLIENT_ID);
  DEBUG_PRINT(F("INFO: MQTT config topic: "));
  DEBUG_PRINTLN(MQTT_CONFIG_TOPIC);
#else

#endif

  sprintf(MQTT_STATE_TOPIC, MQTT_STATE_TOPIC_TEMPLATE, MQTT_CLIENT_ID);
  sprintf(MQTT_COMMAND_TOPIC, MQTT_COMMAND_TOPIC_TEMPLATE, MQTT_CLIENT_ID);
  sprintf(MQTT_STATUS_TOPIC, MQTT_STATUS_TOPIC_TEMPLATE, MQTT_CLIENT_ID);

  DEBUG_PRINT(F("INFO: MQTT state topic: "));
  DEBUG_PRINTLN(MQTT_STATE_TOPIC);
  DEBUG_PRINT(F("INFO: MQTT command topic: "));
  DEBUG_PRINTLN(MQTT_COMMAND_TOPIC);
  DEBUG_PRINT(F("INFO: MQTT status topic: "));
  DEBUG_PRINTLN(MQTT_STATUS_TOPIC);

  mqttClient.setServer(MQTT_SERVER, MQTT_SERVER_PORT);
  mqttClient.setCallback(handleMQTTMessage);

  connectToMQTT();

#if defined(OTA)
  setupOTA();
#endif

  bulb.init();

  cmd = CMD_STATE_CHANGED;
}

void loop() {
#if defined(DEBUG_TELNET)
  // handle the Telnet connection
  handleTelnet();
#endif

  yield();

#if defined(OTA)
  handleOTA();
#endif

  yield();

  connectToMQTT();
  mqttClient.loop();

  yield();

  handleCMD();

  yield();

  bulb.loop();
}


================================================
FILE: ha_mqtt_sensor_dht22/README.md
================================================
# MQTT Sensor - Temperature and Humidity - Home Assistant
A simple example to get temperature and humidity every ten minutes from a DHT22 sensor connected to a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
sensor 1:
  platform: mqtt
  state_topic: 'office/sensor1'
  name: 'Temperature'
  unit_of_measurement: '°C'
  value_template: '{{ value_json.temperature }}'

sensor 2:
  platform: mqtt
  state_topic: 'office/sensor1'
  name: 'Humidity'
  unit_of_measurement: '%'
  value_template: '{{ value_json.humidity }}'
```

## Schematic
- DHT22 leg 1 - VCC
- DHT22 leg 2 - D1/GPIO5 - Resistor 4.7K Ohms - GND
- DHT22 leg 4 - GND
- D0/GPIO16 - RST (wake-up purpose)

![Schematic](Schematic.png)


================================================
FILE: ha_mqtt_sensor_dht22/ha_mqtt_sensor_dht22.ino
================================================
/*
   MQTT Sensor - Temperature and Humidity (DHT22) for Home-Assistant - NodeMCU (ESP8266)
   https://home-assistant.io/components/sensor.mqtt/

   Libraries :
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient
    - DHT : https://github.com/adafruit/DHT-sensor-library
    - ArduinoJson : https://github.com/bblanchon/ArduinoJson

   Sources :
    - File > Examples > ES8266WiFi > WiFiClient
    - File > Examples > PubSubClient > mqtt_auth
    - File > Examples > PubSubClient > mqtt_esp8266
    - File > Examples > DHT sensor library > DHTtester
    - File > Examples > ArduinoJson > JsonGeneratorExample
    - http://www.jerome-bernard.com/blog/2015/10/04/wifi-temperature-sensor-with-nodemcu-esp8266/

   Schematic :
    - https://github.com/mertenats/open-home-automation/blob/master/ha_mqtt_sensor_dht22/Schematic.png
    - DHT22 leg 1 - VCC
    - DHT22 leg 2 - D1/GPIO5 - Resistor 4.7K Ohms - GND
    - DHT22 leg 4 - GND
    - D0/GPIO16 - RST (wake-up purpose)

   Configuration (HA) :
    sensor 1:
      platform: mqtt
      state_topic: 'office/sensor1'
      name: 'Temperature'
      unit_of_measurement: '°C'
      value_template: '{{ value_json.temperature }}'
    
    sensor 2:
      platform: mqtt
      state_topic: 'office/sensor1'
      name: 'Humidity'
      unit_of_measurement: '%'
      value_template: '{{ value_json.humidity }}'

   Samuel M. - v1.1 - 08.2016
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "DHT.h"
#include <ArduinoJson.h>

#define MQTT_VERSION MQTT_VERSION_3_1_1

// Wifi: SSID and password
const char* WIFI_SSID = "[Redacted]";
const char* WIFI_PASSWORD = "[Redacted]";

// MQTT: ID, server IP, port, username and password
const PROGMEM char* MQTT_CLIENT_ID = "office_dht22";
const PROGMEM char* MQTT_SERVER_IP = "[Redacted]";
const PROGMEM uint16_t MQTT_SERVER_PORT = 1883;
const PROGMEM char* MQTT_USER = "[Redacted]";
const PROGMEM char* MQTT_PASSWORD = "[Redacted]";

// MQTT: topic
const PROGMEM char* MQTT_SENSOR_TOPIC = "office/sensor1";

// sleeping time
const PROGMEM uint16_t SLEEPING_TIME_IN_SECONDS = 600; // 10 minutes x 60 seconds

// DHT - D1/GPIO5
#define DHTPIN 5
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);
WiFiClient wifiClient;
PubSubClient client(wifiClient);

// function called to publish the temperature and the humidity
void publishData(float p_temperature, float p_humidity) {
  // create a JSON object
  // doc : https://github.com/bblanchon/ArduinoJson/wiki/API%20Reference
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  // INFO: the data must be converted into a string; a problem occurs when using floats...
  root["temperature"] = (String)p_temperature;
  root["humidity"] = (String)p_humidity;
  root.prettyPrintTo(Serial);
  Serial.println("");
  /*
     {
        "temperature": "23.20" ,
        "humidity": "43.70"
     }
  */
  char data[200];
  root.printTo(data, root.measureLength() + 1);
  client.publish(MQTT_SENSOR_TOPIC, data, true);
  yield();
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
      Serial.println("INFO: connected");
    } else {
      Serial.print("ERROR: failed, rc=");
      Serial.print(client.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  // init the serial
  Serial.begin(115200);

  dht.begin();

  // init the WiFi connection
  Serial.println();
  Serial.println();
  Serial.print("INFO: Connecting to ");
  WiFi.mode(WIFI_STA);
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("INFO: WiFi connected");
  Serial.println("INFO: IP address: ");
  Serial.println(WiFi.localIP());

  // init the MQTT connection
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  if (isnan(h) || isnan(t)) {
    Serial.println("ERROR: Failed to read from DHT sensor!");
    return;
  } else {
    //Serial.println(h);
    //Serial.println(t);
    publishData(t, h);
  }

  Serial.println("INFO: Closing the MQTT connection");
  client.disconnect();

  Serial.println("INFO: Closing the Wifi connection");
  WiFi.disconnect();

  ESP.deepSleep(SLEEPING_TIME_IN_SECONDS * 1000000, WAKE_RF_DEFAULT);
  delay(500); // wait for deep sleep to happen
}


================================================
FILE: ha_mqtt_sensor_photocell/README.md
================================================
# MQTT Sensor - Brightness - Home Assistant
A simple example to get the brightness (0 - 100%) of the room every ten minutes from a photocell connected to a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
sensor 1:
  platform: mqtt
  state_topic: 'office/sensor1'
  name: 'Brightness'
  unit_of_measurement: '%'
  value_template: '{{ value_json.brightness }}'
```

## Schematic
- Photocell leg 1 - VCC
- Photocell leg 2 - A0 - Resistor 10K Ohms - GND
- D0/GPIO16 - RST (wake-up purpose)

![Schematic](Schematic.png)

================================================
FILE: ha_mqtt_sensor_photocell/ha_mqtt_sensor_photocell.ino
================================================
/*
   MQTT Sensor - Brightness (photocell) for Home-Assistant - NodeMCU (ESP8266)
   https://home-assistant.io/components/sensor.mqtt/

   Libraries :
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient
    - ArduinoJson : https://github.com/bblanchon/ArduinoJson

   Sources :
    - File > Examples > ES8266WiFi > WiFiClient
    - File > Examples > PubSubClient > mqtt_auth
    - File > Examples > PubSubClient > mqtt_esp8266
    - File > Examples > ArduinoJson > JsonGeneratorExample

   Schematic :
    - https://github.com/mertenats/open-home-automation/blob/master/ha_mqtt_sensor_photocell/Schematic.png
    - Photocell leg 1 - VCC
    - Photocell leg 2 - A0 - Resistor 10K Ohms - GND
    - D0/GPIO16 - RST (wake-up purpose)

   Configuration (HA) :
    sensor 1:
      platform: mqtt
      state_topic: 'office/sensor1'
      name: 'Brightness'
      unit_of_measurement: '%'
      value_template: '{{ value_json.brightness }}'

   Samuel M. - v1.1 - 08.2016
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

#define MQTT_VERSION MQTT_VERSION_3_1_1

// Wifi: SSID and password
const PROGMEM char* WIFI_SSID = "[Redacted]";
const PROGMEM char* WIFI_PASSWORD = "[Redacted]";

// MQTT: ID, server IP, port, username and password
const PROGMEM char* MQTT_CLIENT_ID = "office_brightness";
const PROGMEM char* MQTT_SERVER_IP = "[Redacted]";
const PROGMEM uint16_t MQTT_SERVER_PORT = 1883;
const PROGMEM char* MQTT_USER = "[Redacted]";
const PROGMEM char* MQTT_PASSWORD = "[Redacted]";

// MQTT: topic
const PROGMEM char* MQTT_SENSOR_TOPIC = "office/sensor1";

// sleeping time
const PROGMEM uint16_t SLEEPING_TIME_IN_SECONDS = 600; // 10 minutes x 60 seconds

// Photocell: A0 
const PROGMEM uint8_t PHOTOCELL_PIN = 0;

WiFiClient wifiClient;
PubSubClient client(wifiClient);

// function called to publish the temperature and the humidity
void publishData(int p_analogRead) {
  // convert 0-1024 into a percentage
  uint8_t brightness = map(p_analogRead, 0, 1024, 0, 100);
  
  // create a JSON object
  // doc : https://github.com/bblanchon/ArduinoJson/wiki/API%20Reference
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["brightness"] = (String)brightness;
  root.prettyPrintTo(Serial);
  Serial.println("");
  /*
  {
    "brightness":  "75"
  }
  */
  char data[200];
  root.printTo(data, root.measureLength() + 1);
  client.publish(MQTT_SENSOR_TOPIC, data);
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
      Serial.println("INFO: connected");
    } else {
      Serial.print("ERROR: failed, rc=");
      Serial.print(client.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  // init the serial
  Serial.begin(115200);

  // init the WiFi connection
  Serial.println();
  Serial.println();
  Serial.print("INFO: Connecting to ");
  WiFi.mode(WIFI_STA);
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("INFO: WiFi connected");
  Serial.println("INFO: IP address: ");
  Serial.println(WiFi.localIP());

  // init the MQTT connection
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // read the value of the photocell
  uint16_t photocell = analogRead(PHOTOCELL_PIN);

  if (photocell < 0 || photocell > 1024) {
    Serial.println("ERROR: Failed to read from the photocell!");
    return;
  } else {
    publishData(photocell);
  }

  Serial.println("INFO: Closing the MQTT connection");
  client.disconnect();

  Serial.println("INFO: Closing the Wifi connection");
  WiFi.disconnect();

  ESP.deepSleep(SLEEPING_TIME_IN_SECONDS * 1000000, WAKE_RF_DEFAULT);
  delay(500); // wait for deep sleep to happen
}


================================================
FILE: ha_mqtt_switch/README.md
================================================
# MQTT Switch - Home Assistant
A simple example to control a switch connected to a NodeMCU board (ESP8266).

## Configuration
configuration.yaml :
```yaml
switch:
  platform: mqtt
  name: 'Office Switch'
  state_topic: 'office/switch1/status'
  command_topic: 'office/switch1/set'
  retain: true
  optimistic: false
```

## Schematic
- Switch leg 1 - VCC
- Switch leg 2 - D1/GPIO5 - Resistor 10K Ohms - GND

![Schematic](Schematic.png)


================================================
FILE: ha_mqtt_switch/ha_mqtt_switch.ino
================================================
/*
   MQTT Switch for Home-Assistant - NodeMCU (ESP8266)
   https://home-assistant.io/components/switch.mqtt/

   Libraries :
    - ESP8266 core for Arduino : https://github.com/esp8266/Arduino
    - PubSubClient : https://github.com/knolleary/pubsubclient
    - OneButton : https://github.com/mathertel/OneButton

   Sources :
    - File > Examples > ES8266WiFi > WiFiClient
    - File > Examples > PubSubClient > mqtt_auth
    - File > Examples > PubSubClient > mqtt_esp8266
    - OneButton : http://www.mathertel.de/Arduino/OneButtonLibrary.aspx

   Schematic :
    - https://github.com/mertenats/open-home-automation/blob/master/ha_mqtt_switch/Schematic.png
    - Switch leg 1 - VCC
    - Switch leg 2 - D1/GPIO5 - Resistor 10K Ohms - GND

   Configuration (HA) :
    switch:
      platform: mqtt
      name: 'Office Switch'
      state_topic: 'office/switch1/status'
      command_topic: 'office/switch1/set'
      retain: true
      optimistic: false

   Samuel M. - v1.1 - 08.2016
   If you like this example, please add a star! Thank you!
   https://github.com/mertenats/open-home-automation
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <OneButton.h>

#define MQTT_VERSION MQTT_VERSION_3_1_1

// Wifi: SSID and password
const char* WIFI_SSID = "[Redacted]";
const char* WIFI_PASSWORD = "[Redacted]";

// MQTT: ID, server IP, port, username and password
const PROGMEM char* MQTT_CLIENT_ID = "office_switch1";
const PROGMEM char* MQTT_SERVER_IP = "[Redacted]";
const PROGMEM uint16_t MQTT_SERVER_PORT = 1883;
const PROGMEM char* MQTT_USER = "[Redacted]";
const PROGMEM char* MQTT_PASSWORD = "[Redacted]";

// MQTT: topics
const PROGMEM char* MQTT_SWITCH_STATUS_TOPIC = "office/switch1/status";
const PROGMEM char* MQTT_SWITCH_COMMAND_TOPIC = "office/switch1/set";

// default payload
const PROGMEM char* SWITCH_ON = "ON";
const PROGMEM char* SWITCH_OFF = "OFF";

// store the state of the switch
boolean m_switch_state = false;

// D1/GPIO5
const PROGMEM uint8_t BUTTON_PIN = 5;

WiFiClient wifiClient;
PubSubClient client(wifiClient);
OneButton button(BUTTON_PIN, false); // false : active HIGH

// function called on button press
// toggle the state of the switch
void click() {
  if (m_switch_state) {
    m_switch_state = false;
  } else {
    m_switch_state = true;
  }
  publishSwitchState();
}

// function called to publish the state of the switch (on/off)
void publishSwitchState() {
  if (m_switch_state) {
    client.publish(MQTT_SWITCH_STATUS_TOPIC, SWITCH_ON, true);
  } else {
    client.publish(MQTT_SWITCH_STATUS_TOPIC, SWITCH_OFF, true);
  }
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
  // concat the payload into a string
  String payload;
  for (uint8_t i = 0; i < p_length; i++) {
    payload.concat((char)p_payload[i]);
  }
  // handle message topic
  if (String(MQTT_SWITCH_COMMAND_TOPIC).equals(p_topic)) {
    // test if the payload is equal to "ON" or "OFF"
    if (payload.equals(String(SWITCH_ON))) {
      if (m_switch_state != true) {
        m_switch_state = true;
        publishSwitchState();
      }
    } else if (payload.equals(String(SWITCH_OFF))) {
      if (m_switch_state != false) {
        m_switch_state = false;
        publishSwitchState();
      }
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("INFO: Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
      Serial.println("INFO: connected");
      
      // Once connected, publish an announcement...
      // publish the initial values
      publishSwitchState();

      // ... and resubscribe
      client.subscribe(MQTT_SWITCH_COMMAND_TOPIC);
    } else {
      Serial.print("ERROR: failed, rc=");
      Serial.print(client.state());
      Serial.println("DEBUG: try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  // init the serial
  Serial.begin(115200);
  
  // link the click function to be called on a single click event.   
  button.attachClick(click);

  // init the WiFi connection
  Serial.println();
  Serial.println();
  Serial.print("INFO: Connecting to ");
  WiFi.mode(WIFI_STA);
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("INFO: WiFi connected");
  Serial.println("INFO: IP address: ");
  Serial.println(WiFi.localIP());

  // init the MQTT connection
  client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT);
  client.setCallback(callback);
}

void loop() {
  // keep watching the push button:
  button.tick();
  delay(10);
  
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}


================================================
FILE: openhome/README.md
================================================
# HOME AUTOMATION WITH OPEN HARDWARE AND SOFTWARE
![Features](images/Features.png)

## Table of contents
1. [Introduction](#introduction)
2. [Automation](#automation)
3. [Parts list](#parts-list)
4. [Installation of the controller](#installation-of-the-controller)
5. [Installation of the extra components for Home Assistant](#installation-of-the-extra-components-for-home-assistant)
6. [Implementation of the actuators and sensors](#implementation-of-the-actuators-and-sensors)
7. [Creation of the automation rules](#creation-of-the-automation-rules)
8. [Demonstration](#demonstration)

## Introduction
Home Automation with Open Hardware and Software (a.k.a. OpenHome) was a school project made in a few weeks by a [student](https://www.linkedin.com/in/mertenats) at College of Engineering and Architecture of Fribourg, Switzerland ([HEIA](https://www.heia-fr.ch)).

### Context
The main goal of this project is to offer an Open Source alternative to commercialized solutions, like Samsung SmartThings or Philips Hue.

In this project, [Home Assistant](https://home-assistant.io) is used as the controller and the [MQTT](http://mqtt.org) protocol is used for the communication between the controller and the actuators/sensors.

The controller is installed on a Raspberry Pi 3 and the actuators/sensors are built on top of [NodeMCU](http://nodemcu.com/index_cn.html) boards (ESP8266).

### Architecture

Network architecture:
![Features](images/Architecture_Network.png)

Note: For simplicity, a NodeMCU module is used to connect two lamps and a sensor. In reality, a module will be used for each lamp or sensor.

Application architecture:
![Features](images/Architecture_MQTT.png)

## Automation
In this project, only the lightning is automated, based on the departure/arrival of the occupiers, the state of the television in the living room or the occupancy of the bed in the bedroom. The lightning is also used to simulate a presence when nobody's home.

Automation rule overview:

- Change the lightning when the TV is switched on or off
- Simulate the sunrise when the alarm clock rings
- Simulate the sunset when the person is going to bed
- Simulate a presence when nobody is at home

The rules are further explained in the chapter [Creation of the automation rules](#creation-of-the-automation-rules).

## Parts list
Hardware:

- 1 Wi-Fi router
- 1 Raspberry Pi
- 3 NodeMCU boards (or any module based on an ESP8266 chip)
- 2 RGB LEDs
- 4 White LEDs
- 1 PIR sensor (HC-SR501)
- 1 Load cell
- 1 Signal amplifier (HX711)
- 1 Photo-resistor
- 1 iBeacon
- 1 iOS device

Software:

- Arduino IDE ([here](https://www.arduino.cc))
- ESP8266 core for Arduino ([here](https://github.com/esp8266/Arduino))
- MQTT library ([here](https://github.com/knolleary/pubsubclient))
- HX711 library ([hre](https://github.com/bogde/HX711))

## Installation of the controller
### Home Assistant
To install Home Assistant easily, a Raspberry Pi All-In-One Installer is available [here](https://home-assistant.io/getting-started/installation-raspberry-pi-all-in-one/). More recently, a Raspberry Pi image was released [here](https://home-assistant.io/blog/2016/10/01/we-have-raspberry-image-now/). This image comes pre-installed with everything we need to get started with Home Assistant. For this project, we just need Home Assistant and the Mosquitto MQTT broker, so I prefer to install everything manually.

First, we need to update the system and install some Python dependencies.

```
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install python-pip python3-dev
sudo pip install --upgrade virtualenv
```

Then, we create a new system user, `hass`, to execute the service for Home Assistant. This is a good practice to reduce the exposure of the rest of the system and provide a more granular control over permissions.

```
sudo adduser --system hass
```

We create a new directory for Home Assistant and we change its ownership to the new created user.

```
sudo mkdir /srv/hass
sudo chown hass /srv/hass
```
We become the new user and we set up a virtual Python environment in the directory we just created. A virtual Python environment is a good practice to avoid interaction with the Python packages used by the system or by others applications. Then, we activate it.

```
sudo su -s /bin/bash hass
virtualenv -p python3 /srv/hass
source /srv/hass/bin/activate
```
And now we are ready to install Home Assistant.

```
pip3 install --upgrade homeassistant
```
Finally we can run Home Assistant by typing the command below.

```
sudo -u hass -H /srv/hass/bin/hass
```

For starting Home Assistant on boot, we need to create a service for `systemd`. Someone has already created one and we can just download it.

```
sudo wget https://raw.githubusercontent.com/home-assistant/home-assistant/master/script/home-assistant%40.service -O /etc/systemd/system/home-assistant@hass.service
```

This service needs a little modification. We have to replace `/usr/bin/hass`with `/srv/hass/bin/hass`. The line in question should look like this now `ExecStart=/srv/hass/bin/hass --runner`.

```
sudo nano /etc/systemd/system/home-assistant@hass.service
```

We need to reload `systemd`to make the daemon aware of the new configuration.

```
sudo systemctl --system daemon-reload
sudo systemctl enable home-assistant@hass
sudo systemctl start home-assistant@hass
```

To upgrade the system in the future, we just need to type the commands below.

```
sudo su -s /bin/bash hass
source /srv/hass/bin/activate
pip3 install --upgrade homeassistant
```

If HTTPS is used (link below), we need to add the following lines into the `configuration.yaml`file.

``` yaml
http:
  api_password: '[REDACTED]'
  ssl_certificate: '/etc/letsencrypt/live/[REDACTED]/fullchain.pem'
  ssl_key: '/etc/letsencrypt/live/[REDACTED]/privkey.pem'
```

Important files

- Configuration file: `/home/hass/.homeassistant/configuration.yaml`
- Logs file: `/home/hass/.homeassistant/home-assistant.log`

The entire configuration is available [here](configuration/home_assistant).

Optional steps:

- Allow a remote access to Home Assistant and protect the GUI with HTTPS ([here](https://home-assistant.io/blog/2015/12/13/setup-encryption-using-lets-encrypt/)).
- Use Tor to make remote access anonymous ([here](https://home-assistant.io/cookbook/tor_configuration/)).

Sources:

- [Installation in Virtualenv](https://home-assistant.io/getting-started/installation-virtualenv/)
- [Autostart Using Systemd](https://home-assistant.io/getting-started/autostart-systemd/)

### Mosquitto MQTT broker
To install the latest version of Mosquitto, we need to use their new repository.

```
wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key
sudo apt-key add mosquitto-repo.gpg.key
```

Then we make the repository available to apt and update its informations.

```
cd /etc/apt/sources.list.d/
sudo wget http://repo.mosquitto.org/debian/mosquitto-jessie.list
sudo apt-get update
```

Finally we can install Mosquitto and its client, for testing purpose.

```
sudo apt-get install mosquitto mosquitto-clients
```

The MQTT protocol provides authentication and ACL functionalities to protect its use.
To create a username/password, we just need to use `mosquitto_passwd`.

```
cd /etc/mosquitto/conf.d/
sudo touch pwfile
sudo mosquitto_passwd pwfile ha
```

And to restrict publishing/subscribing, we need to create a `aclfile`, in which we specify the username and the relevant MQTT topics.

```
cd /etc/mosquitto/conf.d/
sudo touch aclfile
```

ACL examples:

```
user ha
topic write entrance/light1/switch topic write entrance/light2/switch
...
topic read entrance/light1/status
topic read entrance/light2/status
```

If MQTT over TLS (link below), username/password and ACL are used, we need to add the following lines into the `mosquitto.conf`file.

```
allow_anonymous false
password_file /etc/mosquitto/conf.d/pwfile
acl_file /etc/mosquitto/conf.d/aclfile
listener 8883 (optional)
cafile /etc/mosquitto/certs/ca.crt (optional)
certfile /etc/mosquitto/certs/raspberrypi.crt
keyfile /etc/mosquitto/certs/raspberrypi.key (optional)
```

To link Home Assistant with the Mosquitto broker, the `configuration.yaml`file needs the lines below.

```yaml
mqtt:
  broker: 'localhost' #127.0.0.1
  port: 8883 #1883
  client_id: 'ha'
  username: 'ha'
  password: '[REDACTED]' (optional)
  certificate: '/etc/mosquitto/certs/ca.crt' (optional)
```

Important files:

- Configuration file: `/etc/mosquitto/conf.d/mosquitto.conf`
- Logs file: `/var/log/mosquitto/mosquitto.log`

The entire configuration is available [here](configuration/mosquitto).

Optional step:

- Protect MQTT over a secure TLS connection ([here](http://owntracks.org/booklet/guide/broker/)).

Source:

- [Mosquitto Debian repository](https://mosquitto.org/2013/01/mosquitto-debian-repository/
).

### Homebridge
The installation of Homebridge is not mandatory. Homebridge runs on Node.js and this language needs to be installed.

We start by verifying if `git`and `make`are already installed. It's normally the case on Raspbian Jessie.

```
sudo apt-get install git make
```

First, we have to install Node.js.

```
cd Downloads/
wget https://nodejs.org/dist/latest-v4.x/node-v4.6.0-linux-armv6l.tar.gz
tar -xvf node-v4.6.0-linux-armv6l.tar.gz
cd node-v4.6.0-linux-armv6l/
sudo cp -R * /usr/local/
```
And install Avahi and dependencies.

```
sudo apt-get install libavahi-compat-libdnssd-dev screen
```

Finally we install Homebridge and dependencies.

```
sudo npm install -g --unsafe-perm homebridge hap-nodejs node-gyp
cd /usr/local/lib/node_modules/homebridge/
sudo npm install --unsafe-perm bignum
cd /usr/local/lib/node_modules/hap-nodejs/node_modules/mdns/
sudo node-gyp BUILDTYPE=Release rebuild
sudo npm install -g --unsafe-perm homebridge
```

To link Homebridge and Home Assistant together, we have to install this plug-in.

```
sudo npm install -g homebridge-homeassistant
```

For starting Homebridge on boot, we need to create a service for `systemd`. Someone has already created one and we can just download it.

```
cd ~/Downloads
wget https://gist.githubusercontent.com/johannrichard/0ad0de1feb6adb9eb61a/raw/7defd3836f4fbe2b98ea5a97 49c4413d024e9623/homebridge
wget https://gist.githubusercontent.com/johannrichard/0ad0de1feb6adb9eb61a/raw/7defd3836f4fbe2b98ea5a97 49c4413d024e9623/homebridge.service
sudo cp homebridge /etc/default/
sudo cp homebridge.service /etc/systemd/system
```

Then we create a new system user, `homebridge`, to execute the service for Homebridge. We create also a folder `/var/homebridge` for the configuration file.

```
sudo adduser --system homebridge
sudo mkdir /var/homebridge
sudo chown homebridge /var/homebridge
```

We need to create a configuration file for Homebridge. This file will be located in `/var/homebridge`, with the content displayed below.

```
sudo nano /var/homebridge/config.json
```

config.json:

```
{
  "bridge": {
    "name": "Homebridge",
    "username": "CC:22:3D:E3:CE:30",
    "port": 51826,
    "pin": "031-45-154"
  },
  "description": "homebridge-homeassistant",
  "platforms": [
    {
      "platform": "HomeAssistant",
      "name": "HomeAssistant",
      "host": "http://127.0.0.1:8123",
      "password": "yourapipassword",
      "supported_types": ["automation", "binary_sensor", "climate", "cover", "device_tracker", "fan", "group", "input_boolean", "light", "lock", "media_player", "remote", "scene", "script", "sensor", "switch", "vacuum"],
      "default_visibility": "hidden",
      "logging": true,
      "verify_ssl": true
    }
  ]
}
```

We need to reload `systemd`to make the daemon aware of the new configuration.

```
sudo systemctl daemon-reload
sudo systemctl enable homebridge
sudo systemctl start homebridge
```

At the end, the Homebridge bridge should be visible inside Apple's Home application.

Configuration of Homebridge in the Apple Home application:

![Configuration for the Home application](images/Home.png)

Sources:

- [Running HomeBridge on a Raspberry Pi](https://github.com/nfarina/homebridge/wiki/Running-HomeBridge-on-a-Raspberry-Pi)
- [Installing Node.js on a Raspberry Pi 3](https://blog.wia.io/installing-node-js-on-a-raspberry-pi-3)
- [Home Assistant for Homebridge](https://github.com/home-assistant/homebridge-homeassistant)

## Installation of the extra components for Home Assistant
Some extra components have to be installed for providing notifications (with Telegram) with weather conditions (with Forecast.io) to the user and for detecting when the user when is entering/leaving the house (with Owntracks and an iBeacon).

### Telegram
To use Telegram we need to create a bot. The procedure is described [here](https://core.telegram.org/bots).

Configuration for Home Assistant:

```yaml
notify:
  platform: telegram
  api_key: [Redacted]
  chat_id: [Redacted]
```

### ~~Forcast.io~~ Dark Sky
To use the data from ~~Forecast.io~~ darksky.net a [account](https://darksky.net/dev/register) is necessary to retrieve a `api_key`.

Configuration for Home Assistant:

```yaml
sensor:
  - platform: darksky
    api_key: [Redacted]
    monitored_conditions:
      - precip_probability
      - temperature
      - wind_speed
      - cloud_cover
      - humidity
      - pres
Download .txt
gitextract_ko6a3yud/

├── .gitignore
├── LICENSE
├── README.md
├── ha_config_alarm_clock/
│   ├── README.md
│   └── configuration/
│       ├── automations.yaml
│       ├── configuration.yaml
│       ├── customize.yaml
│       ├── groups.yaml
│       ├── input_booleans.yaml
│       ├── input_numbers.yaml
│       ├── media_players.yaml
│       └── sensors.yaml
├── ha_config_zigate/
│   └── README.md
├── ha_mqtt_binary_sensor_ble_scanner/
│   ├── README.md
│   ├── example.config.h
│   └── ha_mqtt_binary_sensor_ble_scanner.ino
├── ha_mqtt_binary_sensor_door/
│   ├── README.md
│   └── ha_mqtt_binary_sensor_door.ino
├── ha_mqtt_binary_sensor_nfc_scanner/
│   ├── README.md
│   ├── example.config.h
│   └── ha_mqtt_binary_sensor_nfc_scanner.ino
├── ha_mqtt_binary_sensor_pir/
│   ├── README.md
│   └── ha_mqtt_binary_sensor_pir.ino
├── ha_mqtt_light/
│   ├── README.md
│   └── ha_mqtt_light.ino
├── ha_mqtt_light_arilux/
│   ├── LICENSE
│   ├── README.md
│   ├── example.config.h
│   └── ha_mqtt_light_arilux.ino
├── ha_mqtt_light_with_WiFiManager_mDNS_and_OTA/
│   ├── README.md
│   └── ha_mqtt_light_with_WiFiManager_mDNS_and_OTA.ino
├── ha_mqtt_light_with_brightness/
│   ├── README.md
│   └── ha_mqtt_light_with_brightness.ino
├── ha_mqtt_multisensor/
│   ├── Configuration/
│   │   ├── example.binary_sensor.yaml
│   │   ├── example.group.yaml
│   │   └── example.sensor.yaml
│   ├── LICENSE
│   ├── MultiSensor.cpp
│   ├── MultiSensor.h
│   ├── README.md
│   ├── example.config .h
│   └── ha_mqtt_multisensor.ino
├── ha_mqtt_rgb_light/
│   ├── README.md
│   └── ha_mqtt_rgb_light.ino
├── ha_mqtt_rgbw_light_with_discovery/
│   ├── README.md
│   ├── config.example.h
│   ├── ha_mqtt_rgbw_light_with_discovery.cpp
│   ├── ha_mqtt_rgbw_light_with_discovery.h
│   └── ha_mqtt_rgbw_light_with_discovery.ino
├── ha_mqtt_sensor_dht22/
│   ├── README.md
│   └── ha_mqtt_sensor_dht22.ino
├── ha_mqtt_sensor_photocell/
│   ├── README.md
│   └── ha_mqtt_sensor_photocell.ino
├── ha_mqtt_switch/
│   ├── README.md
│   └── ha_mqtt_switch.ino
└── openhome/
    ├── README.md
    ├── configuration/
    │   ├── home_assistant/
    │   │   ├── automation/
    │   │   │   ├── automation_1a.yaml
    │   │   │   ├── automation_1b.yaml
    │   │   │   ├── automation_1c.yaml
    │   │   │   ├── automation_1d.yaml
    │   │   │   ├── automation_2a.yaml
    │   │   │   ├── automation_2b.yaml
    │   │   │   ├── automation_3a.yaml
    │   │   │   ├── automation_3b.yaml
    │   │   │   ├── automation_3c.yaml
    │   │   │   └── automation_4a.yaml
    │   │   ├── automation.yaml
    │   │   ├── binary_sensors.yaml
    │   │   ├── configuration.yaml
    │   │   ├── customize.yaml
    │   │   ├── groups.yaml
    │   │   ├── input_numbers.yaml
    │   │   ├── known_devices.yaml
    │   │   ├── lights.yaml
    │   │   └── sensors.yaml
    │   └── mosquitto/
    │       ├── aclfile
    │       ├── mosquitto.conf
    │       └── pwfile
    └── sketches/
        ├── Bedroom/
        │   └── Bedroom.ino
        ├── Entrance/
        │   └── Entrance.ino
        └── Livingroom/
            └── Livingroom.ino
Download .txt
SYMBOL INDEX (9 symbols across 4 files)

FILE: ha_mqtt_multisensor/MultiSensor.cpp
  function doorSensorISR (line 26) | void doorSensorISR(void) {
  function motionSensorISR (line 32) | void motionSensorISR(void) {
  function buttonSensorISR (line 38) | void buttonSensorISR(void) {

FILE: ha_mqtt_multisensor/MultiSensor.h
  function class (line 29) | class MultiSensor {

FILE: ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.cpp
  function Color (line 92) | Color AIRGBWBulb::getColor(void) {

FILE: ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.h
  type Color (line 13) | struct Color {
  type CMD (line 20) | enum CMD {
  type EFFECT (line 31) | enum EFFECT {
  function class (line 36) | class AIRGBWBulb {
Condensed preview — 81 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (301K chars).
[
  {
    "path": ".gitignore",
    "chars": 293,
    "preview": "\r\n.DS_Store\r\nha_mqtt_multisensor/config.h\r\nha_mqtt_multisensor/Configuration/binary_sensor.yaml\r\nha_mqtt_multisensor/Con"
  },
  {
    "path": "LICENSE",
    "chars": 1057,
    "preview": "MIT License\n\nCopyright (c) 2016 \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "README.md",
    "chars": 6714,
    "preview": "# Open Home Automation\n\n**THIS REPOSITORY IS NOT MAINTAINED ANYMORE, SEE [ESPHome](https://esphome.io/) AS AN ALTERNATIV"
  },
  {
    "path": "ha_config_alarm_clock/README.md",
    "chars": 3756,
    "preview": "# Configuration - Alarm Clock - Home Assistant\nA simple configuration example to create an alarm clock and trigger an au"
  },
  {
    "path": "ha_config_alarm_clock/configuration/automations.yaml",
    "chars": 1073,
    "preview": "- alias: 'Turn on the alarm clock'\n  trigger:\n    platform: template\n    value_template: '{{ states.sensor.time.state =="
  },
  {
    "path": "ha_config_alarm_clock/configuration/configuration.yaml",
    "chars": 499,
    "preview": "homeassistant:\n  # Customization file\n  customize: !include customize.yaml\n\nhue:\n  bridges:\n   - host: 192.168.1.130\n\nre"
  },
  {
    "path": "ha_config_alarm_clock/configuration/customize.yaml",
    "chars": 387,
    "preview": "input_number.alarm_clock_hours:\n  friendly_name: 'Heure'\n  icon: mdi:timer\ninput_number.alarm_clock_minutes:\n  friendly_"
  },
  {
    "path": "ha_config_alarm_clock/configuration/groups.yaml",
    "chars": 250,
    "preview": "default_view:\n  view: yes\n  entities:\n    - group.alarm_clock\n\nalarm_clock:\n  name: 'Réveil'\n  entities:\n    - sensor.al"
  },
  {
    "path": "ha_config_alarm_clock/configuration/input_booleans.yaml",
    "chars": 20,
    "preview": "alarm_clock_status:\n"
  },
  {
    "path": "ha_config_alarm_clock/configuration/input_numbers.yaml",
    "chars": 128,
    "preview": "alarm_clock_hours:\n  min: 0\n  max: 23\n  step: 1\n  mode: slider\nalarm_clock_minutes:\n  min: 0\n  max: 55\n  step: 5\n  mode:"
  },
  {
    "path": "ha_config_alarm_clock/configuration/media_players.yaml",
    "chars": 57,
    "preview": "- platform: onkyo\n  host: 192.168.1.133\n  name: 'Stereo'\n"
  },
  {
    "path": "ha_config_alarm_clock/configuration/sensors.yaml",
    "chars": 588,
    "preview": "- platform: template\n  sensors:\n    alarm_clock_hours:\n      value_template: \"{{ states('input_number.alarm_clock_hours'"
  },
  {
    "path": "ha_config_zigate/README.md",
    "chars": 2693,
    "preview": "# Configuration - Zigate and ZigBee devices - Home Assistant\nA simple configuration example to add and control ZigBee de"
  },
  {
    "path": "ha_mqtt_binary_sensor_ble_scanner/README.md",
    "chars": 2426,
    "preview": "# MQTT Binary Sensor - Bluetooth LE Device Tracker - Home Assistant\nA simple example describing how to track a Bluetooth"
  },
  {
    "path": "ha_mqtt_binary_sensor_ble_scanner/example.config.h",
    "chars": 1375,
    "preview": "///////////////////////////////////////////////////////////////////////////\n//  CONFIGURATION - SOFTWARE\n///////////////"
  },
  {
    "path": "ha_mqtt_binary_sensor_ble_scanner/ha_mqtt_binary_sensor_ble_scanner.ino",
    "chars": 7232,
    "preview": "/*\n  MQTT Binary Sensor - Bluetooth LE Device Tracker - Home Assistant\n  \n  Libraries:\n    - PubSubClient: https://githu"
  },
  {
    "path": "ha_mqtt_binary_sensor_door/README.md",
    "chars": 501,
    "preview": "# MQTT Binary Sensor - Door - Home Assistant\nA simple example to monitor the state of a door with a NodeMCU board (ESP82"
  },
  {
    "path": "ha_mqtt_binary_sensor_door/ha_mqtt_binary_sensor_door.ino",
    "chars": 10783,
    "preview": "/* \n  MQTT Binary Sensor - Door sensor for Home-Assistant - NodeMCU (ESP8266)\n  https://home-assistant.io/components/bin"
  },
  {
    "path": "ha_mqtt_binary_sensor_nfc_scanner/README.md",
    "chars": 2788,
    "preview": "# MQTT Binary Sensor - NFC Tags Scanner - Home Assistant\nA simple example describing how to scan NFC tags with an ESP826"
  },
  {
    "path": "ha_mqtt_binary_sensor_nfc_scanner/example.config.h",
    "chars": 1221,
    "preview": "///////////////////////////////////////////////////////////////////////////\n//  CONFIGURATION - SOFTWARE\n///////////////"
  },
  {
    "path": "ha_mqtt_binary_sensor_nfc_scanner/ha_mqtt_binary_sensor_nfc_scanner.ino",
    "chars": 6786,
    "preview": "/*\n  MQTT Binary Sensor - NFC Tags Scanner - Home Assistant\n  \n  Libraries:\n    - PubSubClient: https://github.com/knoll"
  },
  {
    "path": "ha_mqtt_binary_sensor_pir/README.md",
    "chars": 394,
    "preview": "# MQTT Binary Sensor - Motion - Home Assistant\nA simple example to use a PIR motion sensor connected to a NodeMCU board "
  },
  {
    "path": "ha_mqtt_binary_sensor_pir/ha_mqtt_binary_sensor_pir.ino",
    "chars": 3995,
    "preview": "/*\n   MQTT Binary Sensor - Motion (PIR) for Home-Assistant - NodeMCU (ESP8266)\n   https://home-assistant.io/components/b"
  },
  {
    "path": "ha_mqtt_light/README.md",
    "chars": 386,
    "preview": "# MQTT Light - Home Assistant\nA simple example to control a led connected to a NodeMCU board (ESP8266).\n\n## Configuratio"
  },
  {
    "path": "ha_mqtt_light/ha_mqtt_light.ino",
    "chars": 4531,
    "preview": "/*\n   MQTT Light for Home-Assistant - NodeMCU (ESP8266)\n   https://home-assistant.io/components/light.mqtt/\n\n   Librarie"
  },
  {
    "path": "ha_mqtt_light_arilux/LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2017 Samuel Mertenat\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "ha_mqtt_light_arilux/README.md",
    "chars": 2401,
    "preview": "# MQTT Light - Arilux - Home Assistant\nThis sketch is an alternative firmware for Arilux LED controllers, based on the ["
  },
  {
    "path": "ha_mqtt_light_arilux/example.config.h",
    "chars": 1792,
    "preview": "///////////////////////////////////////////////////////////////////////////////////////////////\n// WIFI\n////////////////"
  },
  {
    "path": "ha_mqtt_light_arilux/ha_mqtt_light_arilux.ino",
    "chars": 9401,
    "preview": "/*\n  MQTT Light for Home-Assistant - Arilux (ESP8266)\n   \n  Libraries :\n    - ESP8266 Core : https://github.com/esp8266/"
  },
  {
    "path": "ha_mqtt_light_with_WiFiManager_mDNS_and_OTA/README.md",
    "chars": 1473,
    "preview": "# MQTT Light with WiFiManager, mDNS and OTA - Home Assistant\nA simple example to control the built-in led connected to a"
  },
  {
    "path": "ha_mqtt_light_with_WiFiManager_mDNS_and_OTA/ha_mqtt_light_with_WiFiManager_mDNS_and_OTA.ino",
    "chars": 12071,
    "preview": "/*\n  MQTT Light for Home-Assistant - NodeMCU (ESP8266)\n  https://home-assistant.io/components/switch.mqtt/\n\n  Features:\n"
  },
  {
    "path": "ha_mqtt_light_with_brightness/README.md",
    "chars": 558,
    "preview": "# MQTT Light with brightness support - Home Assistant\nA simple example to control a led connected to a NodeMCU board (ES"
  },
  {
    "path": "ha_mqtt_light_with_brightness/ha_mqtt_light_with_brightness.ino",
    "chars": 5889,
    "preview": "/*\n   MQTT Light (with brightness) for Home-Assistant - NodeMCU (ESP8266)\n   https://home-assistant.io/components/light."
  },
  {
    "path": "ha_mqtt_multisensor/Configuration/example.binary_sensor.yaml",
    "chars": 676,
    "preview": "# Example configuration.yaml entry\nbinary_sensor:\n  - platform: mqtt\n    name: 'Motion'\n    state_topic: '<CHIP_ID>/sens"
  },
  {
    "path": "ha_mqtt_multisensor/Configuration/example.group.yaml",
    "chars": 222,
    "preview": "group:\n  multisensor:\n    name: 'Multisensor'\n    entities:\n      - sensor.temperature\n      - sensor.humidity\n      - s"
  },
  {
    "path": "ha_mqtt_multisensor/Configuration/example.sensor.yaml",
    "chars": 726,
    "preview": "# Example configuration.yml entry\nsensor:\n  - platform: mqtt\n    name: 'Temperature'\n    state_topic: '<CHIP_ID>/sensor/"
  },
  {
    "path": "ha_mqtt_multisensor/LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2017 Samuel Mertenat\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "ha_mqtt_multisensor/MultiSensor.cpp",
    "chars": 8718,
    "preview": "#include \"Arduino.h\"\n#include \"MultiSensor.h\"\n\n#if defined(DHT_SENSOR)\n// https://github.com/adafruit/Adafruit_Sensor\n//"
  },
  {
    "path": "ha_mqtt_multisensor/MultiSensor.h",
    "chars": 1917,
    "preview": "#ifndef MultiSensor_h\n#define MultiSensor_h\n\n#include \"Arduino.h\"\n#include \"config.h\"\n\n#define NO_SENSOR_EVT            "
  },
  {
    "path": "ha_mqtt_multisensor/README.md",
    "chars": 4902,
    "preview": "# MQTT - MultiSensor - Home Assistant\nA full example describing how to monitor your environment with an ESP8266, the MQT"
  },
  {
    "path": "ha_mqtt_multisensor/example.config .h",
    "chars": 3050,
    "preview": "///////////////////////////////////////////////////////////////////////////\n//  CONFIGURATION - HARDWARE\n///////////////"
  },
  {
    "path": "ha_mqtt_multisensor/ha_mqtt_multisensor.ino",
    "chars": 12349,
    "preview": "#include <ESP8266WiFi.h>\n#include \"MultiSensor.h\"\n#include <PubSubClient.h> // https://github.com/knolleary/pubsubclient"
  },
  {
    "path": "ha_mqtt_rgb_light/README.md",
    "chars": 896,
    "preview": "# MQTT RGB Light - Home Assistant\nA simple example to control a RGB led connected to a NodeMCU board (ESP8266).\n\n## Conf"
  },
  {
    "path": "ha_mqtt_rgb_light/ha_mqtt_rgb_light.ino",
    "chars": 8006,
    "preview": "/*\n   MQTT RGB Light for Home-Assistant - NodeMCU (ESP8266)\n   https://home-assistant.io/components/light.mqtt/\n\n   Libr"
  },
  {
    "path": "ha_mqtt_rgbw_light_with_discovery/README.md",
    "chars": 545,
    "preview": "# MQTT JSON Light - Home Assistant\nA simple example to control a RGB led and a white led connected to a NodeMCU board (E"
  },
  {
    "path": "ha_mqtt_rgbw_light_with_discovery/config.example.h",
    "chars": 2119,
    "preview": "///////////////////////////////////////////////////////////////////////////\n//   RGB PINS\n//////////////////////////////"
  },
  {
    "path": "ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.cpp",
    "chars": 7251,
    "preview": "#include \"ha_mqtt_rgbw_light_with_discovery.h\"\n\nvolatile uint8_t effect = EFFECT_NOT_DEFINED;\n\n/////////////////////////"
  },
  {
    "path": "ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.h",
    "chars": 1622,
    "preview": "#pragma once\n#ifndef _RGBW_\n#define _RGBW_\n\n#include <ESP8266WiFi.h>          // https://github.com/esp8266/Arduino\n#inc"
  },
  {
    "path": "ha_mqtt_rgbw_light_with_discovery/ha_mqtt_rgbw_light_with_discovery.ino",
    "chars": 13441,
    "preview": "/*\n  MQTT Discovery and MQTT JSON Light for Home Assistant\n  \n  Samuel Mertenat\n  04.2017\n*/\n\n#include <ESP8266WiFi.h>  "
  },
  {
    "path": "ha_mqtt_sensor_dht22/README.md",
    "chars": 717,
    "preview": "# MQTT Sensor - Temperature and Humidity - Home Assistant\nA simple example to get temperature and humidity every ten min"
  },
  {
    "path": "ha_mqtt_sensor_dht22/ha_mqtt_sensor_dht22.ino",
    "chars": 5246,
    "preview": "/*\n   MQTT Sensor - Temperature and Humidity (DHT22) for Home-Assistant - NodeMCU (ESP8266)\n   https://home-assistant.io"
  },
  {
    "path": "ha_mqtt_sensor_photocell/README.md",
    "chars": 538,
    "preview": "# MQTT Sensor - Brightness - Home Assistant\nA simple example to get the brightness (0 - 100%) of the room every ten minu"
  },
  {
    "path": "ha_mqtt_sensor_photocell/ha_mqtt_sensor_photocell.ino",
    "chars": 4464,
    "preview": "/*\n   MQTT Sensor - Brightness (photocell) for Home-Assistant - NodeMCU (ESP8266)\n   https://home-assistant.io/component"
  },
  {
    "path": "ha_mqtt_switch/README.md",
    "chars": 436,
    "preview": "# MQTT Switch - Home Assistant\nA simple example to control a switch connected to a NodeMCU board (ESP8266).\n\n## Configur"
  },
  {
    "path": "ha_mqtt_switch/ha_mqtt_switch.ino",
    "chars": 4872,
    "preview": "/*\n   MQTT Switch for Home-Assistant - NodeMCU (ESP8266)\n   https://home-assistant.io/components/switch.mqtt/\n\n   Librar"
  },
  {
    "path": "openhome/README.md",
    "chars": 20614,
    "preview": "# HOME AUTOMATION WITH OPEN HARDWARE AND SOFTWARE\n![Features](images/Features.png)\n\n## Table of contents\n1. [Introductio"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_1a.yaml",
    "chars": 475,
    "preview": "alias: 'Switch on the lamp 1 on motion (1a)'\ntrigger:\n  platform: state\n  entity_id: binary_sensor.motion\n  from: 'off'\n"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_1b.yaml",
    "chars": 530,
    "preview": "alias: 'Switch on the lamp 1 & 2 on motion (1b)'\ntrigger:\n  platform: state\n  entity_id: binary_sensor.motion\n  from: 'o"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_1c.yaml",
    "chars": 565,
    "preview": "alias: 'Switch on the lamp 1, 2 & 4 on arrival of an inhabitantn (1c)'\ntrigger:\n  platform: state\n  entity_id: device_tr"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_1d.yaml",
    "chars": 588,
    "preview": "alias: 'Switch on the lamp 1, 2, 5 & 6 on arrival of an inhabitantn (1d)'\ntrigger:\n  platform: state\n  entity_id: device"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_2a.yaml",
    "chars": 403,
    "preview": "alias: 'Switch off the lamp 4 when the TV is turned on (2a)'\ntrigger:\n  platform: state\n  entity_id: binary_sensor.tv\n  "
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_2b.yaml",
    "chars": 341,
    "preview": "alias: 'Switch on the lamp 4 when the TV is turned off (2b)'\ntrigger:\n  platform: state\n  entity_id: binary_sensor.tv\n  "
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_3a.yaml",
    "chars": 3814,
    "preview": "alias: 'Switch on the lamp 5 progressively (3a)'\ntrigger:\n  platform: template\n  value_template: '{{ now.time().strftime"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_3b.yaml",
    "chars": 3068,
    "preview": "alias: 'Switch off the lamp 5 progressively (3b)'\ntrigger:\n  platform: state\n  entity_id: binary_sensor.occupancy\n  from"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_3c.yaml",
    "chars": 349,
    "preview": "alias: 'Switch on the lamp 5 when the person get out of its bed (3c)' \ntrigger:\n  platform: state\n  entity_id: binary_se"
  },
  {
    "path": "openhome/configuration/home_assistant/automation/automation_4a.yaml",
    "chars": 1082,
    "preview": "alias: Simulate presence (4a)'\ntrigger:\n  - platform: state\n    entity_id: group.all_devices\n    from: 'home'\n    to: 'n"
  },
  {
    "path": "openhome/configuration/home_assistant/automation.yaml",
    "chars": 413,
    "preview": "- !include automation/automation_1a.yaml\n- !include automation/automation_1b.yaml\n- !include automation/automation_1c.ya"
  },
  {
    "path": "openhome/configuration/home_assistant/binary_sensors.yaml",
    "chars": 279,
    "preview": "- platform: mqtt\n  name: 'Motion'\n  state_topic: 'entrance/door/motion/status'\n  sensor_class: motion\n- platform: mqtt\n "
  },
  {
    "path": "openhome/configuration/home_assistant/configuration.yaml",
    "chars": 1674,
    "preview": "homeassistant:\n  # Name of the location where Home Assistant is running\n  name: Home\n  # Location required to calculate "
  },
  {
    "path": "openhome/configuration/home_assistant/customize.yaml",
    "chars": 1509,
    "preview": "# lights\nlight.lamp_1:\n  friendly_name: 'Exterior Lamp'\nlight.lamp_2:\n  friendly_name: 'Interior Lamp'\nlight.lamp_3:\n  f"
  },
  {
    "path": "openhome/configuration/home_assistant/groups.yaml",
    "chars": 902,
    "preview": "default_view:\n  view: yes\n  entities:\n    - group.weather\n    - group.occupancy\n    - group.entrance\n    - group.livingr"
  },
  {
    "path": "openhome/configuration/home_assistant/input_numbers.yaml",
    "chars": 127,
    "preview": "alarm_clock_hour:\n  min: 0\n  max: 23\n  step: 1\n  mode: slider\n\nalarm_clock_minute:\n  min: 0\n  max: 55\n  step: 5\n  mode: "
  },
  {
    "path": "openhome/configuration/home_assistant/known_devices.yaml",
    "chars": 82,
    "preview": "\nsamuel_phone:\n  name: Samuel\n  mac: \n  picture: \n  track: yes\n  hide_if_away: no\n"
  },
  {
    "path": "openhome/configuration/home_assistant/lights.yaml",
    "chars": 1334,
    "preview": "# lamp 1\n- platform: mqtt\n  name: 'Lamp 1'\n  state_topic: 'entrance/light1/status'\n  command_topic: 'entrance/light1/swi"
  },
  {
    "path": "openhome/configuration/home_assistant/sensors.yaml",
    "chars": 671,
    "preview": "- platform: forecast\n  api_key: [Redacted]\n  monitored_conditions:\n    - precip_probability\n    - temperature\n    - wind"
  },
  {
    "path": "openhome/configuration/mosquitto/aclfile",
    "chars": 1962,
    "preview": "user ha\ntopic write entrance/light1/switch\ntopic write entrance/light2/switch\n\ntopic write livingroom/light1/switch\ntopi"
  },
  {
    "path": "openhome/configuration/mosquitto/mosquitto.conf",
    "chars": 4110,
    "preview": "# -----------------------------------------------------------------\n# Certificate based SSL/TLS support\n# --------------"
  },
  {
    "path": "openhome/configuration/mosquitto/pwfile",
    "chars": 583,
    "preview": "samuel:$6$HagYska+ZBtV0a2a$RA0pgSHa5g6/NoZqbE5N0UwnV++vmxkgbz5gRd+9J4Vh03PEXqi8XpqnMM9iFJ6QVzH/893VE5E1cI6SVGpung==\nha:$"
  },
  {
    "path": "openhome/sketches/Bedroom/Bedroom.ino",
    "chars": 22437,
    "preview": "/*\n  Configuration for Home Assistant:\n    mqtt:\n      broker: 127.0.0.1\n      port: 1883\n      client_id: 'ha'\n      us"
  },
  {
    "path": "openhome/sketches/Entrance/Entrance.ino",
    "chars": 15218,
    "preview": "/*\n  Configuration for Home Assistant:\n    mqtt:\n      broker: 127.0.0.1\n      port: 1883\n      client_id: 'ha'\n      us"
  },
  {
    "path": "openhome/sketches/Livingroom/Livingroom.ino",
    "chars": 21637,
    "preview": "/*\n  Configuration for Home Assistant:\n    mqtt:\n      broker: 127.0.0.1\n      port: 1883\n      client_id: 'ha'\n      us"
  }
]

About this extraction

This page contains the full source code of the smrtnt/Open-Home-Automation GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 81 files (276.5 KB), approximately 75.2k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!