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 `` and `` with the values defined in `config.h`. ```yaml # Example configuration.yaml entry binary_sensor: - platform: mqtt name: 'Occupancy' state_topic: '/sensor///state' availability_topic: ' 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: /sensor// #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 #include #include // 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 // https://github.com/esp8266/Arduino #include // https://github.com/tzapu/WiFiManager #include // https://github.com/knolleary/pubsubclient/releases/tag/v2.6 #include #include #include #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 `` and `` with the values defined in `config.h`. ```yaml # Example configuration.yaml entry binary_sensor: - platform: mqtt name: 'NFC Tag' state_topic: '/sensor///state' availability_topic: '/availability' - platform: mqtt name: 'NFC Card' state_topic: '/sensor///state' availability_topic: '/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: /sensor// #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 #include #include #include "PN532.h" // https://github.com/Seeed-Studio/PN532 #include // 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 #include #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 #include #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 #include // https://github.com/knolleary/pubsubclient #include // https://github.com/bblanchon/ArduinoJson #include // 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 %h _mqtt._tcp 1883 ``` - 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 #include /* 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 #include #include #include #include // 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: %h _mqtt._tcp 1883 - 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 #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 #include #include #endif // MQTT #define MQTT_VERSION MQTT_VERSION_3_1_1 char MQTT_CLIENT_ID[6] = {0}; // topics: status: /light // command: /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 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 #include #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: '/sensor/motion' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' device_class: motion - platform: mqtt name: 'Door' state_topic: '/sensor/door' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' device_class: opening - platform: mqtt name: 'Button' state_topic: '/sensor/button' availability_topic: '/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: '/sensor/temperature' unit_of_measurement: '°C' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' - platform: mqtt name: 'Humidity' state_topic: '/sensor/humidity' unit_of_measurement: '%' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' - platform: mqtt name: 'Luminosity' state_topic: '/sensor/lux' unit_of_measurement: 'lux' availability_topic: '/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 #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: '/sensor/motion' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' device_class: motion - platform: mqtt name: 'Door' state_topic: '/sensor/door' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' device_class: opening - platform: mqtt name: 'Button' state_topic: '/sensor/button' availability_topic: '/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: '/sensor/temperature' unit_of_measurement: '°C' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' - platform: mqtt name: 'Humidity' state_topic: '/sensor/humidity' unit_of_measurement: '%' availability_topic: '/status' payload_available: 'online' payload_not_available: 'offline' - platform: mqtt name: 'Luminosity' state_topic: '/sensor/lux' unit_of_measurement: 'lux' availability_topic: '/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 #include "MultiSensor.h" #include // https://github.com/knolleary/pubsubclient #if defined(OTA) #include #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 #include #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: /light//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 // 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 // https://github.com/esp8266/Arduino #include // https://github.com/knolleary/pubsubclient #include // https://github.com/bblanchon/ArduinoJson #include #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 #include #include "DHT.h" #include #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 #include #include #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 #include #include #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 - pressure - temperature_max - precip_intensity_max ``` ### Owntracks Configuration for Home Assistant: ```yaml device_tracker: platform: owntracks ``` Configuration for the Owntracks application: ![Configuration for the Owntracks application](images/Owntracks.png) Note: The MQTT broker must be accessible from outside the local network and provide a SSL/TLS secure connexion. Sources: - [Telegram](https://home-assistant.io/components/notify.telegram/) - [Forecast.io](https://home-assistant.io/components/sensor.forecast/) - [Owntracks](https://home-assistant.io/components/device_tracker.owntracks/) ## Implementation of the actuators and sensors The sketches are available [here](sketches). Before using them, we need to modify the Wi-Fi SSID/password, the MQTT username/password, the desired IP address ant the OTA password. The use of TLS is optional. ```C // Wi-Fi: Access Point SSID and password const char* AP_SSID = "[Redacted]"; const char* AP_PASSWORD = "[Redacted]"; ... const char* MQTT_USERNAME = "entrance"; const char* MQTT_PASSWORD = "[Redacted]"; ... // TLS: The fingerprint of the MQTT broker certificate (SHA1) #ifdef TLS // openssl x509 -fingerprint -in .crt const char* CA_FINGERPRINT = "[Redacted]"; // openssl x509 -subject -in .crt const char* CA_SUBJECT = "[Redacted]"; #endif ... const char* OTA_PASSWORD = "[Redacted]"; ``` ### Entrance #### Schematic LED on the left: - Shortest leg to GND - Longest leg to D1, with a 220 Ohms resistor LED on the right: - Shortest leg to GND - Longest leg to D2, with a 220 Ohms resistor PIR sensor: - Red cable to VIN - Yellow cable to D3 - Black cable to GND ![Schematic of the entrance module](sketches/Entrance/Schematic.png) #### Configuration for Home Assistant ```yaml light: # lamp 1 - platform: mqtt name: 'Lamp 1' state_topic: 'entrance/light1/status' command_topic: 'entrance/light1/switch' optimistic: false # lamp 2 - platform: mqtt name: 'Lamp 2' state_topic: 'entrance/light2/status' command_topic: 'entrance/light2/switch' optimistic: false binary_sensor: platform: mqtt name: 'Motion' state_topic: 'entrance/door/motion/status' sensor_class: motion ``` ### Living room #### Schematic LED on the left: - Longest leg to GND - Leg on the left to D1, with a 220 Ohms resistor - Leg on the middle to D2, with a 220 Ohms resistor - Leg on the right to D3, with a 220 Ohms resistor LED on the right: - Shortest leg to GND - Longest leg to D4, with a 220 Ohms resistor Photo-resistor: - One leg to VCC - The other leg to GND, with a 10K Ohms resistor and a cable to A0 ![Schematic of the living room module](sketches/Livingroom/Schematic.png) #### Configuration for Home Assistant ```yaml light: # lamp 3 (RGB) - platform: mqtt name: 'Lamp 3' state_topic: 'livingroom/light1/status' command_topic: 'livingroom/light1/switch' brightness_state_topic: 'livingroom/light1/brightness/status' brightness_command_topic: 'livingroom/light1/brightness/set' rgb_state_topic: 'livingroom/light1/color/status' rgb_command_topic: 'livingroom/light1/color/set' optimistic: false # lamp 4 - platform: mqtt name: 'Lamp 4' state_topic: 'livingroom/light2/status' command_topic: 'livingroom/light2/switch' optimistic: false binary_sensor: platform: mqtt name: 'TV' state_topic: 'livingroom/tv/status' ``` ### Bedroom #### Schematic LED on the left: - Longest leg to GND - Leg on the left to D1, with a 220 Ohms resistor - Leg on the middle to D2, with a 220 Ohms resistor - Leg on the right to D3, with a 220 Ohms resistor LED on the right: - Shortest leg to GND - Longest leg to D4, with a 220 Ohms resistor Load cell to HX711: - Red cable to E+ - Black cable to E- - White cable to A- - Green cable to A+ HX711: - VCC to VCC - GND to GND - SCK to D6 - DT to D5 ![Schematic of the bedroom module](sketches/Bedroom/Schematic.png) #### Configuration for Home Assistant ```yaml light: # lamp 5 (RGB) - platform: mqtt name: 'Lamp 5' state_topic: 'bedroom/light1/status' command_topic: 'bedroom/light1/switch' brightness_state_topic: 'bedroom/light1/brightness/status' brightness_command_topic: 'bedroom/light1/brightness/set' rgb_state_topic: 'bedroom/light1/color/status' rgb_command_topic: 'bedroom/light1/color/set' optimistic: false # lamp 6 - platform: mqtt name: 'Lamp 6' state_topic: 'bedroom/light2/status' command_topic: 'bedroom/light2/switch' optimistic: false binary_sensor: platform: mqtt name: 'Occupancy' state_topic: 'bedroom/bed/occupancy/status' sensor_class: occupancy ``` ## Creation of the automation rules ### Entrance | Scenario | Description | File | | -------- |-------------| -----| | 1a | Turn on the light 1 when a person is detected if nobody's home, the sun is below and the person is unknown | [here](configuration/home_assistant/automation/automation_1a.yaml) | | 1b | Turn on the lights 1 & 2 when a person is detected if a occupier's at home, the sun is below and the person is unknown | [here](configuration/home_assistant/automation/automation_1b.yaml) | | 1c | Turn on the lights 1, 2 & 4 when a occupier of the home is detected if the sun is below and it's before 22:30 | [here](configuration/home_assistant/automation/automation_1c.yaml) | | 1d | Turn on the lights 1, 2, 5 & 6 when a occupier of the home is detected if the sun is below and it's after 22:30 | [here](configuration/home_assistant/automation/automation_1d.yaml) | ### Living room | Scenario | Description | File | | -------- |-------------| -----| | 2a | Set the brightness of the light 3 to 50% and turn off the light 4 when the TV is turned on if the sun is below | [here](configuration/home_assistant/automation/automation_2a.yaml) | | 2b | Set the brightness of the light 3 to 75% when the TV is turned off if the sun is below | [here](configuration/home_assistant/automation/automation_2b.yaml) | ### Bedroom | Scenario | Description | File | | -------- |-------------| -----| | 3a | Simulate a sunrise with the light 5 when the alarm clock rings, switch off the light 5 and switch on the light 6, send the weather conditions to the user| [here](configuration/home_assistant/automation/automation_3a.yaml) | | 3b | Simulate a sunset when the occupier is detected in its bed, switch off all the others lights| [here](configuration/home_assistant/automation/automation_3b.yaml) | ### Presence simulation | Scenario | Description | File | | -------- |-------------| -----| | 4a | Simulate a presence when nobody's home | [here](configuration/home_assistant/automation/automation_4a.yaml) | ## Demonstration Features: - 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 - Control the system with Apple Home application and Siri [![OpenHome with Home Assistant and MQTT](images/Youtube.png)](https://www.youtube.com/watch?v=Vh-vzFPCF2U "OpenHome with Home Assistant and MQTT") *If you like the content of this repo, please add a star! Thank you!* ================================================ FILE: openhome/configuration/home_assistant/automation/automation_1a.yaml ================================================ alias: 'Switch on the lamp 1 on motion (1a)' trigger: platform: state entity_id: binary_sensor.motion from: 'off' to: 'on' condition: condition: and conditions: - condition: state entity_id: group.all_devices state: 'not_home' - condition: state entity_id: 'sun.sun' state: 'below_horizon' action: - service: light.turn_on entity_id: light.lamp_1 - delay: minutes: 3 - service: light.turn_off entity_id: light.lamp_1 ================================================ FILE: openhome/configuration/home_assistant/automation/automation_1b.yaml ================================================ alias: 'Switch on the lamp 1 & 2 on motion (1b)' trigger: platform: state entity_id: binary_sensor.motion from: 'off' to: 'on' condition: condition: and conditions: - condition: state entity_id: group.all_devices state: 'home' - condition: state entity_id: 'sun.sun' state: 'below_horizon' action: - service: light.turn_on entity_id: - light.lamp_1 - light.lamp_2 - delay: minutes: 3 - service: light.turn_off entity_id: - light.lamp_1 - light.lamp_2 ================================================ FILE: openhome/configuration/home_assistant/automation/automation_1c.yaml ================================================ alias: 'Switch on the lamp 1, 2 & 4 on arrival of an inhabitantn (1c)' trigger: platform: state entity_id: device_tracker.samuel_phone from: 'not_home' to: 'home' condition: condition: and conditions: - condition: time before: '22:30:00' - condition: state entity_id: sun.sun state: 'below_horizon' action: - service: light.turn_on entity_id: - light.lamp_1 - light.lamp_2 - light.lamp_4 - delay: minutes: 3 - service: light.turn_off entity_id: - light.lamp_1 - light.lamp_2 ================================================ FILE: openhome/configuration/home_assistant/automation/automation_1d.yaml ================================================ alias: 'Switch on the lamp 1, 2, 5 & 6 on arrival of an inhabitantn (1d)' trigger: platform: state entity_id: device_tracker.samuel_phone from: 'not_home' to: 'home' condition: condition: and conditions: - condition: time after: '22:30:00' - condition: state entity_id: sun.sun state: 'below_horizon' action: - service: light.turn_on entity_id: - light.lamp_1 - light.lamp_2 - light.lamp_5 - light.lamp_6 - delay: minutes: 3 - service: light.turn_off entity_id: - light.lamp_1 - light.lamp_2 ================================================ FILE: openhome/configuration/home_assistant/automation/automation_2a.yaml ================================================ alias: 'Switch off the lamp 4 when the TV is turned on (2a)' trigger: platform: state entity_id: binary_sensor.tv from: 'off' to: 'on' condition: condition: state entity_id: sun.sun state: 'below_horizon' action: - service: light.turn_off entity_id: light.lamp_4 - service: light.turn_on data: entity_id: light.lamp_3 brightness: 128 rgb_color: [0, 255, 0] ================================================ FILE: openhome/configuration/home_assistant/automation/automation_2b.yaml ================================================ alias: 'Switch on the lamp 4 when the TV is turned off (2b)' trigger: platform: state entity_id: binary_sensor.tv from: 'on' to: 'off' condition: condition: state entity_id: sun.sun state: 'below_horizon' action: service: light.turn_on data: entity_id: light.lamp_3 brightness: 192 rgb_color: [255, 255, 255] ================================================ FILE: openhome/configuration/home_assistant/automation/automation_3a.yaml ================================================ alias: 'Switch on the lamp 5 progressively (3a)' trigger: platform: template value_template: '{{ now.time().strftime("%-H") == states.sensor.alarm_clock_hour.state and now.time().strftime("%-M") == states.sensor.alarm_clock_minute.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: # minute 0 to 1 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 45 rgb_color: [255, 0, 0] # red - delay: minutes: 1 # minute 1 to 2 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 60 rgb_color: [255, 51, 51] - delay: minutes: 1 # minute 2 to 3 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 75 rgb_color: [255, 102, 102] - delay: minutes: 1 # minute 3 to 4 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 90 rgb_color: [255, 128, 0] - delay: minutes: 1 # minute 4 to 5 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 105 rgb_color: [255, 153, 51] - delay: minutes: 1 # minute 5 to 6 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 120 rgb_color: [255, 178, 102] - delay: minutes: 1 # minute 6 to 7 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 135 rgb_color: [255, 204, 153] - delay: minutes: 1 # minute 7 to 8 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 150 rgb_color: [255, 229, 204] - delay: minutes: 1 # minute 8 to 9 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 165 rgb_color: [255, 255, 204] - delay: minutes: 1 # minute 9 to 10 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 180 rgb_color: [255, 255, 153] - delay: minutes: 1 # minute 10 to 11 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 195 rgb_color: [255, 255, 102] - delay: minutes: 1 # minute 11 to 12 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 210 rgb_color: [255, 255, 51] - delay: minutes: 1 # minute 12 to 13 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 225 rgb_color: [255, 255, 0] - delay: minutes: 1 # minute 13 to 14 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 240 rgb_color: [255, 255, 255] - delay: minutes: 1 # minute 14 to 15 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 255 rgb_color: [255, 255, 255] - delay: minutes: 1 - service: light.turn_on entity_id: light.lamp_6 - service: light.turn_off entity_id: light.lamp_5 # https://home-assistant.io/cookbook/notify_if_over_threshold/ - service: notify.notify data_template: message: > Hi Sam ! It's time to wake up ! It's {{ states.sensor.forecastio_temperature.state | round(0) }} degrees Celcius outside and the maximum temperature will be {{ states.sensor.forecastio_daily_high_temperature.state | round(0) }}. There's a {{ states.sensor.forecastio_precip_probability.state | round(0) }}% chance of rain today. {% if states('sensor.forecastio_precip_probability') | int > 50 %} Don't forget to bring your favorite umbrella! Have a nice (and rainy) day ! {% else %} Have a nice day! {% endif %} ================================================ FILE: openhome/configuration/home_assistant/automation/automation_3b.yaml ================================================ alias: 'Switch off the lamp 5 progressively (3b)' trigger: platform: state entity_id: binary_sensor.occupancy from: 'off' to: 'on' condition: condition: time after: '20:00:00' before: '07:00:00' action: - service: light.turn_off # minute 0 to 1 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 255 rgb_color: [255, 255, 255] - delay: minutes: 1 # minute 1 to 2 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 240 rgb_color: [255, 255, 255] - delay: minutes: 1 # minute 2 to 3 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 225 rgb_color: [255, 255, 0] - delay: minutes: 1 # minute 3 to 4 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 210 rgb_color: [255, 255, 51] - delay: minutes: 1 # minute 4 to 5 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 195 rgb_color: [255, 255, 102] - delay: minutes: 1 # minute 5 to 6 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 180 rgb_color: [255, 255, 153] - delay: minutes: 1 # minute 6 to 7 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 165 rgb_color: [255, 255, 204] - delay: minutes: 1 # minute 7 to 8 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 150 rgb_color: [255, 229, 204] - delay: minutes: 1 # minute 8 to 9 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 135 rgb_color: [255, 204, 153] - delay: minutes: 1 # minute 9 to 10 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 120 rgb_color: [255, 178, 102] - delay: minutes: 1 # minute 10 to 11 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 105 rgb_color: [255, 153, 51] - delay: minutes: 1 # minute 11 to 12 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 90 rgb_color: [255, 128, 0] - delay: minutes: 1 # minute 12 to 13 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 75 rgb_color: [255, 102, 102] - delay: minutes: 1 # minute 13 to 14 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 60 rgb_color: [255, 51, 51] - delay: minutes: 1 # minute 14 to 15 - service: light.turn_on data: entity_id: light.lamp_5 brightness: 45 rgb_color: [255, 0, 0] # red - delay: minutes: 1 - service: light.turn_off # set the light 5 into its initial state (withe, brightness 100%) - service: mqtt.publish data: topic: 'bedroom/light1/color/set' payload: '255,255,255' - service: mqtt.publish data: topic: 'bedroom/light1/brightness/set' payload: '255' ================================================ FILE: openhome/configuration/home_assistant/automation/automation_3c.yaml ================================================ alias: 'Switch on the lamp 5 when the person get out of its bed (3c)' trigger: platform: state entity_id: binary_sensor.occupancy from: 'on' to: 'off' condition: condition: time after: '20:00:00' before: '07:00:00' action: service: light.turn_on data: entity_id: light.lamp_5 brightness: 64 rgb_color: [255, 255, 255] ================================================ FILE: openhome/configuration/home_assistant/automation/automation_4a.yaml ================================================ alias: Simulate presence (4a)' trigger: - platform: state entity_id: group.all_devices from: 'home' to: 'not_home' - platform: sun event: sunset - platform: event event_type: event_simulate_presence condition: condition: and conditions: - condition: state entity_id: 'sun.sun' state: 'below_horizon' - condition: time before: '23:01:00' - condition: state entity_id: group.all_devices state: 'not_home' action: # https://home-assistant.io/cookbook/perform_actions_based_on_input_select/ # https://home-assistant.io/getting-started/scripts/ - service: light.turn_on data_template: entity_id: > light.lamp_{{ (range(1, 6) | random) }} # wait some seconds (will be in reality in minutes) - delay: '00:{{ (range(2, 10) | random) }}:00' # turn off all devices - service: light.turn_off # wait a little bit before turning on another lamp - delay: '00:00:{{ (range(1, 5) | random) }}' # generate an event to call again this automation rule - event: event_simulate_presence ================================================ FILE: openhome/configuration/home_assistant/automation.yaml ================================================ - !include automation/automation_1a.yaml - !include automation/automation_1b.yaml - !include automation/automation_1c.yaml - !include automation/automation_1d.yaml - !include automation/automation_2a.yaml - !include automation/automation_2b.yaml - !include automation/automation_3a.yaml - !include automation/automation_3b.yaml - !include automation/automation_3c.yaml - !include automation/automation_4a.yaml ================================================ FILE: openhome/configuration/home_assistant/binary_sensors.yaml ================================================ - platform: mqtt name: 'Motion' state_topic: 'entrance/door/motion/status' sensor_class: motion - platform: mqtt name: 'Occupancy' state_topic: 'bedroom/bed/occupancy/status' sensor_class: occupancy - platform: mqtt name: 'TV' state_topic: 'livingroom/tv/status' ================================================ FILE: openhome/configuration/home_assistant/configuration.yaml ================================================ homeassistant: # Name of the location where Home Assistant is running name: Home # Location required to calculate the time the sun rises and sets latitude: [REDACTED] longitude: [REDACTED] # Impacts weather/sunrise data elevation: [REDACTED] # metric for Metric, imperial for Imperial unit_system: metric # Pick yours from here: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones time_zone: Europe/Zurich customize: !include customize.yaml # Show links to resources in log and frontend # introduction: # Enables the frontend frontend: http: api_password: '[REDACTED]' ssl_certificate: '/etc/letsencrypt/live/[REDACTED]/fullchain.pem' ssl_key: '/etc/letsencrypt/live/[REDACTED]/privkey.pem' # Checks for available updates updater: # Discover some devices automatically discovery: # Allows you to issue voice commands from the frontend in enabled browsers conversation: # Enables support for tracking state changes over time. history: # View all events in a logbook logbook: # Track the sun sun: mqtt: broker: 'localhost' #127.0.0.1 port: 8883 #1883 client_id: 'ha' username: 'ha' password: '[REDACTED]' certificate: '/etc/mosquitto/certs/ca.crt' discovery: true # optional discovery_prefix: homeassistant # optional light: !include lights.yaml sensor: !include sensors.yaml binary_sensor: !include binary_sensors.yaml group: !include groups.yaml automation: !include automation.yaml input_number: !include input_sliders.yaml input_boolean: alarm_clock_status: initial: on notify: platform: telegram api_key: [REDACTED] chat_id: [REDACTED] device_tracker: platform: owntracks ================================================ FILE: openhome/configuration/home_assistant/customize.yaml ================================================ # lights light.lamp_1: friendly_name: 'Exterior Lamp' light.lamp_2: friendly_name: 'Interior Lamp' light.lamp_3: friendly_name: 'Cosy Lamp' light.lamp_4: friendly_name: 'Ceiling Lamp' light.lamp_5: friendly_name: 'Bedside Lamp' light.lamp_6: friendly_name: 'Principal Lamp' # binary sensors # icon: https://materialdesignicons.com binary_sensor.occupancy: friendly_name: 'Bed Occupancy' icon: mdi:hotel binary_sensor.tv: friendly_name: 'Television' device_tracker.samuel_phone: friendly_name: 'Samuel' sun.sun: hidden: true # Forecast.io sensor.forecastio_cloud_coverage: hidden: true sensor.forecastio_daily_high_temperature: hidden: true sensor.forecastio_daily_max_precip_intensity: hidden: true sensor.forecastio_humidity: friendly_name: 'Humidity' icon: mdi:weather-rainy sensor.forecastio_precip_probability: hidden: true sensor.forecastio_pressure: friendly_name: 'Pressure' icon: mdi:nature sensor.forecastio_temperature: friendly_name: 'Temperature' icon: mdi:thermometer sensor.forecastio_wind_speed: hidden: true # alarm clock input_slider.alarm_clock_hour: friendly_name: 'Hour' icon: mdi:timer input_slider.alarm_clock_minute: friendly_name: 'Minute' icon: mdi:timer input_boolean.alarm_clock_status: friendly_name: 'Alarm Clock Status (Week only)' icon: mdi:alarm-check sensor.alarm_clock_hour: hidden: true sensor.alarm_clock_minute: hidden: true sensor.alarm_clock_time: friendly_name: 'Alarm Clock Setting' icon: mdi:alarm ================================================ FILE: openhome/configuration/home_assistant/groups.yaml ================================================ default_view: view: yes entities: - group.weather - group.occupancy - group.entrance - group.livingroom - group.bedroom - group.alarm_clock weather: name: 'Weather' entities: - sensor.forecastio_temperature - sensor.forecastio_humidity - sensor.forecastio_pressure occupancy: name: 'Occupancy' entities: - device_tracker.samuel_phone entrance: name: 'Entrance' entities: - binary_sensor.motion - light.lamp_1 - light.lamp_2 livingroom: name: 'Living room' entities: - binary_sensor.tv - light.lamp_3 - light.lamp_4 bedroom: name: 'Bedroom' entities: - binary_sensor.occupancy - light.lamp_5 - light.lamp_6 alarm_clock: name: 'Alarm Clock' entities: - sensor.alarm_clock_time - input_slider.alarm_clock_hour - input_slider.alarm_clock_minute - input_boolean.alarm_clock_status ================================================ FILE: openhome/configuration/home_assistant/input_numbers.yaml ================================================ alarm_clock_hour: min: 0 max: 23 step: 1 mode: slider alarm_clock_minute: min: 0 max: 55 step: 5 mode: slider ================================================ FILE: openhome/configuration/home_assistant/known_devices.yaml ================================================ samuel_phone: name: Samuel mac: picture: track: yes hide_if_away: no ================================================ FILE: openhome/configuration/home_assistant/lights.yaml ================================================ # lamp 1 - platform: mqtt name: 'Lamp 1' state_topic: 'entrance/light1/status' command_topic: 'entrance/light1/switch' optimistic: false # lamp 2 - platform: mqtt name: 'Lamp 2' state_topic: 'entrance/light2/status' command_topic: 'entrance/light2/switch' optimistic: false # lamp 3 (RGB) - platform: mqtt name: 'Lamp 3' state_topic: 'livingroom/light1/status' command_topic: 'livingroom/light1/switch' brightness_state_topic: 'livingroom/light1/brightness/status' brightness_command_topic: 'livingroom/light1/brightness/set' rgb_state_topic: 'livingroom/light1/color/status' rgb_command_topic: 'livingroom/light1/color/set' optimistic: false # lamp 4 - platform: mqtt name: 'Lamp 4' state_topic: 'livingroom/light2/status' command_topic: 'livingroom/light2/switch' optimistic: false # lamp 5 (RGB) - platform: mqtt name: 'Lamp 5' state_topic: 'bedroom/light1/status' command_topic: 'bedroom/light1/switch' brightness_state_topic: 'bedroom/light1/brightness/status' brightness_command_topic: 'bedroom/light1/brightness/set' rgb_state_topic: 'bedroom/light1/color/status' rgb_command_topic: 'bedroom/light1/color/set' optimistic: false # lamp 6 - platform: mqtt name: 'Lamp 6' state_topic: 'bedroom/light2/status' command_topic: 'bedroom/light2/switch' optimistic: false ================================================ FILE: openhome/configuration/home_assistant/sensors.yaml ================================================ - platform: forecast api_key: [Redacted] monitored_conditions: - precip_probability - temperature - wind_speed - cloud_cover - humidity - pressure - temperature_max - precip_intensity_max - platform: template sensors: alarm_clock_hour: value_template: '{{ states("input_number.alarm_clock_hour") | round(0) }}' alarm_clock_minute: value_template: '{{ states("input_number.alarm_clock_minute") | round(0) }}' alarm_clock_time: value_template: '{{ states("sensor.alarm_clock_hour") }}:{% if states("sensor.alarm_clock_minute")|length == 1 %}0{% endif %}{{ states("sensor.alarm_clock_minute") }}' ================================================ FILE: openhome/configuration/mosquitto/aclfile ================================================ user ha topic write entrance/light1/switch topic write entrance/light2/switch topic write livingroom/light1/switch topic write livingroom/light1/brightness/set topic write livingroom/light1/color/set topic write livingroom/light2/switch topic write bedroom/light1/switch topic write bedroom/light1/brightness/set topic write bedroom/light1/color/set topic write bedroom/light2/switch topic read entrance/light1/status topic read entrance/light2/status topic read entrance/door/motion/status topic read owntracks/samuel/phone topic read owntracks/samuel/phone/event topic read livingroom/light1/status topic read livingroom/light1/brightness/status topic read livingroom/light1/color/status topic read livingroom/light2/status topic read livingroom/tv/status topic read bedroom/light1/status topic read bedroom/light1/brightness/status topic read bedroom/light1/color/status topic read bedroom/light2/status topic read bedroom/bed/occupancy/status user entrance topic write entrance/light1/status topic write entrance/light2/status topic write entrance/door/motion/status topic read entrance/light1/switch topic read entrance/light2/switch user livingroom topic write livingroom/light1/status topic write livingroom/light1/brightness/status topic write livingroom/light1/color/status topic write livingroom/light2/status topic write livingroom/tv/status topic read livingroom/light1/switch topic read livingroom/light1/brightness/set topic read livingroom/light1/color/set topic read livingroom/light2/switch user bedroom topic write bedroom/light1/status topic write bedroom/light1/brightness/status topic write bedroom/light1/color/status topic write bedroom/light2/status topic write bedroom/bed/occupancy/status topic read bedroom/light1/switch topic read bedroom/light1/brightness/set topic read bedroom/light1/color/set topic read bedroom/light2/switch user samuel topic write owntracks/samuel/phone topic write owntracks/samuel/phone/event ================================================ FILE: openhome/configuration/mosquitto/mosquitto.conf ================================================ # ----------------------------------------------------------------- # Certificate based SSL/TLS support # ----------------------------------------------------------------- # The following options can be used to enable SSL/TLS support for # this listener. Note that the recommended port for MQTT over TLS # is 8883, but this must be set manually. # # See also the mosquitto-tls man page. #listener 1883 listener 8883 # At least one of cafile or capath must be defined. They both # define methods of accessing the PEM encoded Certificate # Authority certificates that have signed your server certificate # and that you wish to trust. # cafile defines the path to a file containing the CA certificates. # capath defines a directory that will be searched for files # containing the CA certificates. For capath to work correctly, the # certificate files must have ".crt" as the file ending and you must run # "c_rehash " each time you add/remove a certificate. #cafile #capath cafile /etc/mosquitto/certs/ca.crt # Path to the PEM encoded server certificate. #certfile certfile /etc/mosquitto/certs/raspberrypi.crt # Path to the PEM encoded keyfile. #keyfile keyfile /etc/mosquitto/certs/raspberrypi.key # ================================================================= # Security # ================================================================= # Boolean value that determines whether clients that connect # without providing a username are allowed to connect. If set to # false then a password file should be created (see the # password_file option) to control authenticated client access. # Defaults to true. allow_anonymous false # ----------------------------------------------------------------- # Default authentication and topic access control # ----------------------------------------------------------------- # Control access to the broker using a password file. This file can be # generated using the mosquitto_passwd utility. If TLS support is not compiled # into mosquitto (it is recommended that TLS support should be included) then # plain text passwords are used, in which case the file should be a text file # with lines in the format: # username:password # The password (and colon) may be omitted if desired, although this # offers very little in the way of security. # # See the TLS client require_certificate and use_identity_as_username options # for alternative authentication options. password_file /etc/mosquitto/conf.d/pwfile # Control access to topics on the broker using an access control list # file. If this parameter is defined then only the topics listed will # have access. # If the first character of a line of the ACL file is a # it is treated as a # comment. # Topic access is added with lines of the format: # # topic [read|write|readwrite] # # The access type is controlled using "read", "write" or "readwrite". This # parameter is optional (unless contains a space character) - if not # given then the access is read/write. can contain the + or # # wildcards as in subscriptions. # # The first set of topics are applied to anonymous clients, assuming # allow_anonymous is true. User specific topic ACLs are added after a # user line as follows: # # user # # The username referred to here is the same as in password_file. It is # not the clientid. # # # If is also possible to define ACLs based on pattern substitution within the # topic. The patterns available for substition are: # # %c to match the client id of the client # %u to match the username of the client # # The substitution pattern must be the only text for that level of hierarchy. # # The form is the same as for the topic keyword, but using pattern as the # keyword. # Pattern ACLs apply to all users even if the "user" keyword has previously # been given. # # If using bridges with usernames and ACLs, connection messages can be allowed # with the following pattern: # pattern write $SYS/broker/connection/%c/state # # pattern [read|write|readwrite] # # Example: # # pattern write sensor/%u/data # acl_file /etc/mosquitto/conf.d/aclfile ================================================ FILE: openhome/configuration/mosquitto/pwfile ================================================ samuel:$6$HagYska+ZBtV0a2a$RA0pgSHa5g6/NoZqbE5N0UwnV++vmxkgbz5gRd+9J4Vh03PEXqi8XpqnMM9iFJ6QVzH/893VE5E1cI6SVGpung== ha:$6$kvipp6VVJOrqYesh$NOji6P9/91OroPkJdjLcwGo115b0JWuFcC6NoFlVVf+Fd7p07XrAdpDtstOMtDly9acPE3NqrHLSV8/RHwswLw== entrance:$6$hxoat1lWtyYxbwwA$DnikUcSz/aIZh12zz0GM0blj2G2EXcv67K4qylmrKCofmahgCQRp7HzkEeqiWsAobu8xic9lynZPOUigDIUtAQ== livingroom:$6$L6286ehtOInn7J/b$Mxeng6igWWzt7Wb6Y+fo58Yukz09O90Kvu5AwsysNycJIj8ETLV66OGNLkQ8e105D10mR4nMz+DeP8fgPL5jHA== bedroom:$6$CBpKfzH+97D9j5fM$9zUS/WH/HZBL9WYTUA8XKVkoDnP6q4ObxOILvyPiWiHVFlq03/oNt+TCo1KS62+amN/l5AhJOpSaUY2yi3BedA== ================================================ FILE: openhome/sketches/Bedroom/Bedroom.ino ================================================ /* Configuration for Home Assistant: mqtt: broker: 127.0.0.1 port: 1883 client_id: 'ha' username: 'ha' password: '!ƒha@OpenHome16' light: # lamp 5 (RGB) - platform: mqtt name: 'Lamp 5' state_topic: 'bedroom/light1/status' command_topic: 'bedroom/light1/switch' brightness_state_topic: 'bedroom/light1/brightness/status' brightness_command_topic: 'bedroom/light1/brightness/set' rgb_state_topic: 'bedroom/light1/color/status' rgb_command_topic: 'bedroom/light1/color/set' optimistic: false # lamp 6 - platform: mqtt name: 'Lamp 6' state_topic: 'bedroom/light2/status' command_topic: 'bedroom/light2/switch' optimistic: false binary_sensor: - platform: mqtt name: 'Occupancy' state_topic: 'bedroom/bed/occupancy/status' sensor_class: occupancy Sources: - MQTT: File > Examples > PubSubClient > mqtt_esp8266 - TLS: https://io.adafruit.com/blog/security/2016/07/05/adafruit-io-security-esp8266/ - OTA: File > Examples > ArduinoOTA > BasicOTA - HX711: https://github.com/sparkfun/HX711-Load-Cell-Amplifier/blob/master/firmware/SparkFun_HX711_Example/SparkFun_HX711_Example.ino */ #include // https://github.com/esp8266/Arduino (GNUv2.1 licence) #include // https://github.com/knolleary/pubsubclient (no licence) #include "HX711.h" // https://github.com/bogde/HX711 (GNUv2 licence) #include //#define DEBUG #define TLS #define MQTT_VERSION MQTT_VERSION_3_1_1 // Wi-Fi: Access Point SSID and password const char* AP_SSID = "[Redacted]"; const char* AP_PASSWORD = "[Redacted]"; // MQTT: client ID, broker IP address, port, username & password const char* MQTT_CLIENT_ID = "bedroom"; const char* MQTT_SERVER_IP = "192.168.1.10"; #ifdef TLS const uint16_t MQTT_SERVER_PORT = 8883; #else const uint16_t MQTT_SERVER_PORT = 1883; #endif const char* MQTT_USERNAME = "bedroom"; const char* MQTT_PASSWORD = "[Redacted]"; // MQTT: topics // lamp 5 const char* TOPIC_LIGHT1_STATUS = "bedroom/light1/status"; const char* TOPIC_LIGHT1_BRIGHTNESS_STATUS = "bedroom/light1/brightness/status"; const char* TOPIC_LIGHT1_COLOR_STATUS = "bedroom/light1/color/status"; const char* TOPIC_LIGHT1_COMMAND = "bedroom/light1/switch"; const char* TOPIC_LIGHT1_BRIGHTNESS_COMMAND = "bedroom/light1/brightness/set"; const char* TOPIC_LIGHT1_COLOR_COMMAND = "bedroom/light1/color/set"; // lamp 6 const char* TOPIC_LIGHT2_STATUS = "bedroom/light2/status"; const char* TOPIC_LIGHT2_COMMAND = "bedroom/light2/switch"; // load cell const char* TOPIC_LOAD_CELL_STATUS = "bedroom/bed/occupancy/status"; // MQTT: payloads // Lamps 3 & 4: "ON"/"OFF" const char* PAYLOAD_ON = "ON"; const char* PAYLOAD_OFF = "OFF"; boolean g_light1_status = false; // turn off by default uint8_t g_light1_brightness = 254; // max value by default uint8_t g_light1_color_red = 255; uint8_t g_light1_color_green = 255; uint8_t g_light1_color_blue = 255; boolean g_light2_status = false; float g_load_cell_value = 0; boolean g_load_cell_status = false; // nobody in the bed, by default uint32_t g_load_cell_last_change = 0; boolean g_load_cell_status_to_publish = false; // scalling factor for the load cell // calibration sketch: https://github.com/sparkfun/HX711-Load-Cell-Amplifier/tree/master/firmware/SparkFun_HX711_Calibration const int LOAD_CELL_SCALING_FACTOR = -7050; const uint8_t LOAD_CELL_THRESHOLD = 5; // +/- 5 lbs const uint16_t LOAD_CELL_TIME_OFFSET = 3000; // 3 seconds in the same state before a notification to the controller // buffer used to send/receive data with MQTT const uint8_t MSG_BUFFER_SIZE = 20; char g_msg_buffer[MSG_BUFFER_SIZE]; const uint8_t LIGHT1_RED_PIN = D3; const uint8_t LIGHT1_GREEN_PIN = D2; const uint8_t LIGHT1_BLUE_PIN = D1; const uint8_t LIGHT2_PIN = D7; // D4 seems to be also connected to a internal LED... const uint8_t LOAD_CELL_DOUT_PIN = D6; const uint8_t LOAD_CELL_CLK_PIN = D5; // TLS: The fingerprint of the MQTT broker certificate (SHA1) #ifdef TLS // openssl x509 -fingerprint -in .crt const char* CA_FINGERPRINT = "[Redacted]"; // openssl x509 -subject -in .crt const char* CA_SUBJECT = "[Redacted]"; #endif // Fixed IP address: IP address, IP gateway, subnet, dns const IPAddress IP (192, 168, 1, 102); const IPAddress IP_GATEWAY (192, 168, 1, 1); const IPAddress IP_SUBNET (255, 255, 255, 0); const IPAddress IP_DNS (192, 168, 1, 1); // OTA: Hostname (MQTT_CLIENT_ID) & password const char* OTA_PASSWORD = "[Redacted]"; // WiFiFlientSecure instead of WiFiClient, for SSL/TLS support #ifdef TLS WiFiClientSecure g_wifiClient; #else WiFiClient g_wifiClient; #endif PubSubClient g_mqttClient(g_wifiClient); HX711 g_loadCell(LOAD_CELL_DOUT_PIN, LOAD_CELL_CLK_PIN); /////////////////////////////////////////////////////////////////////////// // // LIGHT 1 (RGB) // /////////////////////////////////////////////////////////////////////////// /* Function called to publish the status of the light 1 */ void publishLight1Status() { if (g_light1_status) { if (g_mqttClient.publish(TOPIC_LIGHT1_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_LIGHT1_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /* Function called to publish the brightness status of the light 1 */ void publishLight1BrightnessStatus() { snprintf(g_msg_buffer, MSG_BUFFER_SIZE, "%d", g_light1_brightness); if (g_mqttClient.publish(TOPIC_LIGHT1_BRIGHTNESS_STATUS, g_msg_buffer, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_BRIGHTNESS_STATUS); Serial.print(F(". Payload: ")); Serial.println(g_msg_buffer); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } /* Function called to publish the color status of the light 1 */ void publishLight1ColorStatus() { snprintf(g_msg_buffer, MSG_BUFFER_SIZE, "%d,%d,%d", g_light1_color_red, g_light1_color_green, g_light1_color_blue); if (g_mqttClient.publish(TOPIC_LIGHT1_COLOR_STATUS, g_msg_buffer, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_COLOR_STATUS); Serial.print(F(". Payload: ")); Serial.println(g_msg_buffer); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } /* Function called to set the color of the light 1 @param p_light1_color_red The value for the red channel @param p_light1_color_green The value for the green channel @param p_light1_color_blue The value for the blue channel */ void setLight1Color(uint8_t p_light1_color_red, uint8_t p_light1_color_green, uint8_t p_light1_color_blue) { analogWrite(LIGHT1_RED_PIN, p_light1_color_red * g_light1_brightness / 255); analogWrite(LIGHT1_GREEN_PIN, map(p_light1_color_green, 0, 255, 0, g_light1_brightness)); analogWrite(LIGHT1_BLUE_PIN, map(p_light1_color_blue, 0, 255, 0, g_light1_brightness)); } /////////////////////////////////////////////////////////////////////////// // // LIGHT 2 // /////////////////////////////////////////////////////////////////////////// /* Function called to publish the status of the light 2 */ void publishLight2Status() { if (g_light2_status) { if (g_mqttClient.publish(TOPIC_LIGHT2_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT2_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_LIGHT2_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT2_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /* Function called to switch the status of the light 2 */ void setLight2Status() { if (g_light2_status) { digitalWrite(LIGHT2_PIN, HIGH); } else { digitalWrite(LIGHT2_PIN, LOW); } } /////////////////////////////////////////////////////////////////////////// // // LOAD CELL // /////////////////////////////////////////////////////////////////////////// /* Function called to read the load cell */ void readLoadCell() { g_load_cell_value = g_loadCell.get_units(); if (g_load_cell_value > LOAD_CELL_THRESHOLD || g_load_cell_value < -LOAD_CELL_THRESHOLD) { // if someone was already in bed... if (g_load_cell_status) { // if someone was already in bed... // test if he was in the bed for the specified duration if (millis() > g_load_cell_last_change + LOAD_CELL_TIME_OFFSET) { if (g_load_cell_status_to_publish) { g_load_cell_status_to_publish = false; publishLoadCellStatus(); } } } else { g_load_cell_last_change = millis(); g_load_cell_status = true; g_load_cell_status_to_publish = true; } } else { if (g_load_cell_status) { g_load_cell_last_change = millis(); g_load_cell_status = false; g_load_cell_status_to_publish = true; } else { if (millis() > g_load_cell_last_change + LOAD_CELL_TIME_OFFSET) { if (g_load_cell_status_to_publish) { g_load_cell_status_to_publish = false; publishLoadCellStatus(); } } } } #ifdef DEBUG Serial.print(F("INFO: Load cell read value: ")); Serial.println(g_load_cell_value); #endif } /* Function called to publish the status of the occupancy of the bed */ void publishLoadCellStatus() { if (g_load_cell_status) { if (g_mqttClient.publish(TOPIC_LOAD_CELL_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LOAD_CELL_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_LOAD_CELL_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LOAD_CELL_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /////////////////////////////////////////////////////////////////////////// // // WIFI and TLS // /////////////////////////////////////////////////////////////////////////// /* Function called to setup the connection to the Wi-Fi Access Point */ void setupWifi() { delay(10); // attempt to connect to the Wi-Fi AP WiFi.mode(WIFI_STA); WiFi.begin(AP_SSID, AP_PASSWORD); // define the fixed IP address WiFi.config(IP, IP_GATEWAY, IP_SUBNET, IP_DNS); while (WiFi.status() != WL_CONNECTED) { delay(500); } #ifdef DEBUG Serial.println(F("INFO: Client is now connected to the Wi-Fi AP")); Serial.print(F("INFO: IP address: ")); Serial.println(WiFi.localIP()); #endif #ifdef TLS verifyFingerprint(); #endif } /* Function called to verify the fingerprint of the MQTT server and establish a secure connection */ #ifdef TLS void verifyFingerprint() { if (!g_wifiClient.connect(MQTT_SERVER_IP, MQTT_SERVER_PORT)) { #ifdef DEBUG Serial.println(F("ERROR: The connection failed to the secure MQTT server")); #endif return; } if (g_wifiClient.verify(CA_FINGERPRINT, CA_SUBJECT)) { #ifdef DEBUG Serial.println(F("INFO: The connection is secure")); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: The given certificate does't match")); #endif } } #endif /////////////////////////////////////////////////////////////////////////// // // MQTT // /////////////////////////////////////////////////////////////////////////// /* Function called when a MQTT message 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 callback(char* p_topic, byte* p_payload, unsigned int p_length) { #ifdef DEBUG Serial.println(F("INFO: A new MQTT message arrived")); Serial.print(F("INFO: Topic: ")); Serial.println(p_topic); Serial.print(F("INFO: Payload: ")); for (int i = 0; i < p_length; i++) { Serial.print((char)p_payload[i]); } Serial.println(); Serial.print(F("INFO: Length: ")); Serial.println(p_length); #endif // handle the MQTT topic of the received message if (String(TOPIC_LIGHT1_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } if (payload.equals(String(PAYLOAD_ON))) { if (g_light1_status != true) { g_light1_status = true; setLight1Color(g_light1_color_red, g_light1_color_green, g_light1_color_blue); publishLight1Status(); } } else if (payload.equals(String(PAYLOAD_OFF))) { if (g_light1_status != false) { g_light1_status = false; setLight1Color(LOW, LOW, LOW); // switch off the light publishLight1Status(); } } } else if (String(TOPIC_LIGHT1_BRIGHTNESS_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } uint8_t brightness = payload.toInt(); if (brightness < 0 || brightness > 255) { // do nothing... return; } else { g_light1_brightness = brightness; setLight1Color(g_light1_color_red, g_light1_color_green, g_light1_color_blue); publishLight1BrightnessStatus(); } } else if (String(TOPIC_LIGHT1_COLOR_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } // get the position of the first and second comma uint8_t firstIndex = payload.indexOf(','); uint8_t lastIndex = payload.lastIndexOf(','); // get the value for the red color uint8_t red_color = payload.substring(0, firstIndex).toInt(); if (red_color < 0 || red_color > 255) { return; } else { g_light1_color_red = red_color; } // get the value for the green color uint8_t green_color = payload.substring(firstIndex + 1, lastIndex).toInt(); if (green_color < 0 || green_color > 255) { return; } else { g_light1_color_green = green_color; } // get the value for the blue color uint8_t blue_color = payload.substring(lastIndex + 1).toInt(); if (blue_color < 0 || blue_color > 255) { return; } else { g_light1_color_blue = blue_color; } setLight1Color(g_light1_color_red, g_light1_color_green, g_light1_color_blue); publishLight1ColorStatus(); } else if (String(TOPIC_LIGHT2_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } if (payload.equals(String(PAYLOAD_ON))) { g_light2_status = true; setLight2Status(); publishLight2Status(); } else if (payload.equals(String(PAYLOAD_OFF))) { g_light2_status = false; setLight2Status(); publishLight2Status(); } else { #ifdef DEBUG Serial.println(F("ERROR: The payload of the MQTT message is not valid")); #endif } } else { // do nothing..... #ifdef DEBUG Serial.println(F("INFO: The received MQTT message was not used")); #endif } } /* Function called to reconnect the client to the MQTT broker and publish/subscribe to/from some MQTT topics */ void reconnect() { while (!g_mqttClient.connected()) { if (g_mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) { #ifdef DEBUG Serial.println(F("INFO: The client is successfully connected to the MQTT broker")); #endif // subscribe to the light1 command topic if (g_mqttClient.subscribe(TOPIC_LIGHT1_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT1_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT1_COMMAND); #endif } // subscribe to the light1 brightness command topic if (g_mqttClient.subscribe(TOPIC_LIGHT1_BRIGHTNESS_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT1_BRIGHTNESS_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT1_BRIGHTNESS_COMMAND); #endif } // subscribe to the light1 brightness command topic if (g_mqttClient.subscribe(TOPIC_LIGHT1_COLOR_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT1_COLOR_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT1_COLOR_COMMAND); #endif } // subscribe to the light2 command topic if (g_mqttClient.subscribe(TOPIC_LIGHT2_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT2_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT2_COMMAND); #endif } // set the initial status of lights 1 & 2 setLight1Color(LOW, LOW, LOW); setLight2Status(); // publish the initial status of lights 1 & 2 publishLight1Status(); publishLight1BrightnessStatus(); publishLight1ColorStatus(); publishLight2Status(); publishLoadCellStatus(); } else { #ifdef DEBUG Serial.println(F("ERROR: The connection failed with the MQTT broker")); Serial.print("ERROR: rc: "); Serial.println(g_mqttClient.state()); // wait 5 seconds before retrying delay(5000); #endif } } } /////////////////////////////////////////////////////////////////////////// // // SETUP and LOOP // /////////////////////////////////////////////////////////////////////////// /* Function called once to initialize the board */ void setup() { #ifdef DEBUG Serial.begin(115200); Serial.println(F("\nINFO: The Wi-Fi module is starting...")); #endif // init the LEDs as output pinMode(LIGHT1_RED_PIN, OUTPUT); pinMode(LIGHT1_GREEN_PIN, OUTPUT); pinMode(LIGHT1_BLUE_PIN, OUTPUT); pinMode(LIGHT2_PIN, OUTPUT); setupWifi(); // a wdt reset occurs during the startup // the setup function takes probably more than 1 second // no solution founded yet -> a hard reset is necessary (button rst) // https://github.com/esp8266/Arduino/issues/34 //ESP.wdtDisable(); //yield(); // init the load cell g_loadCell.set_scale(LOAD_CELL_SCALING_FACTOR); g_loadCell.tare(); //ESP.wdtEnable(WDTO_8S); //ESP.wdtFeed(); // set the MQTT broker IP address and port g_mqttClient.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT); // set the MQTT callback function g_mqttClient.setCallback(callback); // set the hostname & password for OTA ArduinoOTA.setHostname(MQTT_CLIENT_ID); ArduinoOTA.setPassword(OTA_PASSWORD); #ifdef DEBUG ArduinoOTA.onStart([]() { Serial.println(F("INFO: OTA starts")); }); ArduinoOTA.onEnd([]() { Serial.println(F("INFO: OTA ends")); }); ArduinoOTA.onError([](ota_error_t error) { Serial.println(F("INFO: An error occurs during OTA")); }); #endif ArduinoOTA.begin(); // blink the internal LED to indicate the end of the startup pinMode(BUILTIN_LED, OUTPUT); digitalWrite(BUILTIN_LED, LOW); delay(100); digitalWrite(BUILTIN_LED, HIGH); delay(100); digitalWrite(BUILTIN_LED, LOW); delay(100); digitalWrite(BUILTIN_LED, HIGH); } /* Function called infinitely after the setup function */ void loop() { // keep the MQTT client connected to the broker if (!g_mqttClient.connected()) { reconnect(); } g_mqttClient.loop(); yield(); //delay(150); readLoadCell(); yield(); ArduinoOTA.handle(); yield(); } ================================================ FILE: openhome/sketches/Entrance/Entrance.ino ================================================ /* Configuration for Home Assistant: mqtt: broker: 127.0.0.1 port: 1883 client_id: 'ha' username: 'ha' password: '!ha@OpenHome16' light: - platform: mqtt name: 'Lamp 1' state_topic: 'entrance/light1/status' command_topic: 'entrance/light1/switch' optimistic: false - platform: mqtt name: 'Lamp 2' state_topic: 'entrance/light2/status' command_topic: 'entrance/light2/switch' optimistic: false binary_sensor: - platform: mqtt name: 'Motion' state_topic: 'entrance/door/motion/status' sensor_class: motion Sources: - MQTT: File > Examples > PubSubClient > mqtt_esp8266 - TLS: https://io.adafruit.com/blog/security/2016/07/05/adafruit-io-security-esp8266/ - OTA: File > Examples > ArduinoOTA > BasicOTA - PIR: https://learn.adafruit.com/pir-passive-infrared-proximity-motion-sensor/using-a-pir */ #include // https://github.com/esp8266/Arduino (GNUv2.1 licence) #include // https://github.com/knolleary/pubsubclient (no licence) #include //#define DEBUG #define TLS #define MQTT_VERSION MQTT_VERSION_3_1_1 // Wi-Fi: Access Point SSID and password const char* AP_SSID = "[Redacted]"; const char* AP_PASSWORD = "[Redacted]"; // MQTT: client ID, broker IP address, port, username & password const char* MQTT_CLIENT_ID = "entrance"; const char* MQTT_SERVER_IP = "192.168.1.10"; #ifdef TLS const uint16_t MQTT_SERVER_PORT = 8883; #else const uint16_t MQTT_SERVER_PORT = 1883; #endif const char* MQTT_USERNAME = "entrance"; const char* MQTT_PASSWORD = "[Redacted]"; // MQTT: topics // lamp 1 const char* TOPIC_LIGHT1_STATUS = "entrance/light1/status"; const char* TOPIC_LIGHT1_COMMAND = "entrance/light1/switch"; // lamp 2 const char* TOPIC_LIGHT2_STATUS = "entrance/light2/status"; const char* TOPIC_LIGHT2_COMMAND = "entrance/light2/switch"; // motion sensor const char* TOPIC_MOTION_STATUS = "entrance/door/motion/status"; // MQTT: payloads // Lamps 1 & 2 + motion sensor: "ON"/"OFF" const char* PAYLOAD_ON = "ON"; const char* PAYLOAD_OFF = "OFF"; boolean g_light1_status = false; // turn off by default boolean g_light2_status = false; volatile boolean g_motion_status = false; // no motion by default volatile boolean g_motion_change = false; // no publication to do by default const uint8_t LIGHT1_PIN = D1; const uint8_t LIGHT2_PIN = D2; const uint8_t MOTION_SENSOR_PIN = D8; // connected before to D3, strange behaviour when D1/D2 was HIGH // TLS: The fingerprint of the MQTT broker certificate (SHA1) #ifdef TLS // openssl x509 -fingerprint -in .crt const char* CA_FINGERPRINT = "[Redacted]"; // openssl x509 -subject -in .crt const char* CA_SUBJECT = "[Redacted]"; #endif // Fixed IP address: IP address, IP gateway, subnet, dns const IPAddress IP (192, 168, 1, 100); const IPAddress IP_GATEWAY (192, 168, 1, 1); const IPAddress IP_SUBNET (255, 255, 255, 0); const IPAddress IP_DNS (192, 168, 1, 1); // OTA: Hostname (MQTT_CLIENT_ID) & password const char* OTA_PASSWORD = "[Redacted]"; // WiFiFlientSecure instead of WiFiClient, for SSL/TLS support #ifdef TLS WiFiClientSecure g_wifiClient; #else WiFiClient g_wifiClient; #endif PubSubClient g_mqttClient(g_wifiClient); /////////////////////////////////////////////////////////////////////////// // // LIGHT 1 // /////////////////////////////////////////////////////////////////////////// /* Function called to publish the status of the light 1 */ void publishLight1Status() { if (g_light1_status) { if (g_mqttClient.publish(TOPIC_LIGHT1_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_LIGHT1_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /* Function called to switch the status of the light 1 */ void setLight1Status() { if (g_light1_status) { digitalWrite(LIGHT1_PIN, HIGH); } else { digitalWrite(LIGHT1_PIN, LOW); } } /////////////////////////////////////////////////////////////////////////// // // LIGHT 2 // /////////////////////////////////////////////////////////////////////////// /* Function called to publish the status of the light 2 */ void publishLight2Status() { if (g_light2_status) { if (g_mqttClient.publish(TOPIC_LIGHT2_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT2_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_LIGHT2_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT2_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /* Function called to switch the status of the light 2 */ void setLight2Status() { if (g_light2_status) { digitalWrite(LIGHT2_PIN, HIGH); } else { digitalWrite(LIGHT2_PIN, LOW); } } /////////////////////////////////////////////////////////////////////////// // // MOTION SENSOR // /////////////////////////////////////////////////////////////////////////// /* Function called when a motion is detected/ended */ void onMotionChanged() { g_motion_status = !g_motion_status; g_motion_change = true; } /* Function called to publish the status of the motion sensor */ void publishMotionStatus() { if (g_motion_status) { if (g_mqttClient.publish(TOPIC_MOTION_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_MOTION_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_MOTION_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_MOTION_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /////////////////////////////////////////////////////////////////////////// // // WIFI and TLS // /////////////////////////////////////////////////////////////////////////// /* Function called to setup the connection to the Wi-Fi Access Point */ void setupWifi() { delay(10); // attempt to connect to the Wi-Fi AP WiFi.mode(WIFI_STA); WiFi.begin(AP_SSID, AP_PASSWORD); // define the fixed IP address WiFi.config(IP, IP_GATEWAY, IP_SUBNET, IP_DNS); while (WiFi.status() != WL_CONNECTED) { delay(500); } #ifdef DEBUG Serial.println(F("INFO: Client is now connected to the Wi-Fi AP")); Serial.print(F("INFO: IP address: ")); Serial.println(WiFi.localIP()); #endif #ifdef TLS verifyFingerprint(); #endif } /* Function called to verify the fingerprint of the MQTT server and establish a secure connection */ #ifdef TLS void verifyFingerprint() { if (!g_wifiClient.connect(MQTT_SERVER_IP, MQTT_SERVER_PORT)) { #ifdef DEBUG Serial.println(F("ERROR: The connection failed to the secure MQTT server")); #endif return; } if (g_wifiClient.verify(CA_FINGERPRINT, CA_SUBJECT)) { #ifdef DEBUG Serial.println(F("INFO: The connection is secure")); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: The given certificate does't match")); #endif } } #endif /////////////////////////////////////////////////////////////////////////// // // MQTT // /////////////////////////////////////////////////////////////////////////// /* Function called when a MQTT message 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 callback(char* p_topic, byte* p_payload, unsigned int p_length) { #ifdef DEBUG Serial.println(F("INFO: A new MQTT message arrived")); Serial.print(F("INFO: Topic: ")); Serial.println(p_topic); Serial.print(F("INFO: Payload: ")); for (int i = 0; i < p_length; i++) { Serial.print((char)p_payload[i]); } Serial.println(); Serial.print(F("INFO: Length: ")); Serial.println(p_length); #endif // handle the MQTT topic of the received message if (String(TOPIC_LIGHT1_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } if (payload.equals(String(PAYLOAD_ON))) { g_light1_status = true; setLight1Status(); publishLight1Status(); } else if (payload.equals(String(PAYLOAD_OFF))) { g_light1_status = false; setLight1Status(); publishLight1Status(); } else { #ifdef DEBUG Serial.println(F("ERROR: The payload of the MQTT message is not valid")); #endif } } else if (String(TOPIC_LIGHT2_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } if (payload.equals(String(PAYLOAD_ON))) { g_light2_status = true; setLight2Status(); publishLight2Status(); } else if (payload.equals(String(PAYLOAD_OFF))) { g_light2_status = false; setLight2Status(); publishLight2Status(); } else { #ifdef DEBUG Serial.println(F("ERROR: The payload of the MQTT message is not valid")); #endif } } else { // do nothing..... #ifdef DEBUG Serial.println(F("INFO: The received MQTT message was not used")); #endif } } /* Function called to reconnect the client to the MQTT broker and publish/subscribe to/from some MQTT topics */ void reconnect() { while (!g_mqttClient.connected()) { if (g_mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) { #ifdef DEBUG Serial.println(F("INFO: The client is successfully connected to the MQTT broker")); #endif // subscribe to the light1 command topic if (g_mqttClient.subscribe(TOPIC_LIGHT1_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT1_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT1_COMMAND); #endif } // subscribe to the light2 command topic if (g_mqttClient.subscribe(TOPIC_LIGHT2_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT2_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT2_COMMAND); #endif } // set the initial status of lights 1 & 2 setLight1Status(); setLight2Status(); // publish the initial status of lights 1 & 2 publishLight1Status(); publishLight2Status(); } else { #ifdef DEBUG Serial.println(F("ERROR: The connection failed with the MQTT broker")); Serial.print("ERROR: rc: "); Serial.println(g_mqttClient.state()); // wait 5 seconds before retrying delay(5000); #endif } } } /////////////////////////////////////////////////////////////////////////// // // SETUP and LOOP // /////////////////////////////////////////////////////////////////////////// /* Function called once to initialize the board */ void setup() { #ifdef DEBUG Serial.begin(115200); Serial.println(F("\nINFO: The Wi-Fi module is starting...")); #endif // init the LEDs as output, the motion sensor as input pinMode(LIGHT1_PIN, OUTPUT); pinMode(LIGHT2_PIN, OUTPUT); pinMode(MOTION_SENSOR_PIN, INPUT); attachInterrupt(digitalPinToInterrupt(MOTION_SENSOR_PIN), onMotionChanged, CHANGE); pinMode(BUILTIN_LED, OUTPUT); // the built-in LED is used to indicate a motion digitalWrite(BUILTIN_LED, HIGH); // turn off the built-in LED setupWifi(); // set the MQTT broker IP address and port g_mqttClient.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT); // set the MQTT callback function g_mqttClient.setCallback(callback); // set the hostname & password for OTA ArduinoOTA.setHostname(MQTT_CLIENT_ID); ArduinoOTA.setPassword(OTA_PASSWORD); #ifdef DEBUG ArduinoOTA.onStart([]() { Serial.println(F("INFO: OTA starts")); }); ArduinoOTA.onEnd([]() { Serial.println(F("INFO: OTA ends")); }); ArduinoOTA.onError([](ota_error_t error) { Serial.println(F("INFO: An error occurs during OTA")); }); #endif ArduinoOTA.begin(); // blink the internal LED to indicate the end of the startup digitalWrite(BUILTIN_LED, LOW); delay(100); digitalWrite(BUILTIN_LED, HIGH); delay(100); digitalWrite(BUILTIN_LED, LOW); delay(100); digitalWrite(BUILTIN_LED, HIGH); } /* Function called infinitely after the setup function */ void loop() { // keep the MQTT client connected to the broker if (!g_mqttClient.connected()) { reconnect(); } g_mqttClient.loop(); yield(); ArduinoOTA.handle(); yield(); // test if the status of the motion sensor was changed by an interrupt if (g_motion_change) { publishMotionStatus(); g_motion_change = false; if (g_motion_status) { digitalWrite(BUILTIN_LED, LOW); // turn on the built-in LED #ifdef DEBUG Serial.println(F("INFO: Motion detected")); #endif } else { digitalWrite(BUILTIN_LED, HIGH); // turn off the built-in LED #ifdef DEBUG Serial.println(F("INFO: Motion ended")); #endif } } yield(); } ================================================ FILE: openhome/sketches/Livingroom/Livingroom.ino ================================================ /* Configuration for Home Assistant: mqtt: broker: 127.0.0.1 port: 1883 client_id: 'ha' username: 'ha' password: '!ha@OpenHome16' light: # lamp 3 (RGB) - platform: mqtt name: 'Lamp 3' state_topic: 'livingroom/light1/status' command_topic: 'livingroom/light1/switch' brightness_state_topic: 'livingroom/light1/brightness/status' brightness_command_topic: 'livingroom/light1/brightness/set' rgb_state_topic: 'livingroom/light1/color/status' rgb_command_topic: 'livingroom/light1/color/set' optimistic: false # lamp 4 - platform: mqtt name: 'Lamp 4' state_topic: 'livingroom/light2/status' command_topic: 'livingroom/light2/switch' optimistic: false binary_sensor: - platform: mqtt name: 'TV' state_topic: 'livingroom/tv/status' Sources: - MQTT: File > Examples > PubSubClient > mqtt_esp8266 - TLS: https://io.adafruit.com/blog/security/2016/07/05/adafruit-io-security-esp8266/ - OTA: File > Examples > ArduinoOTA > BasicOTA */ #include // https://github.com/esp8266/Arduino (GNUv2.1 licence) #include // https://github.com/knolleary/pubsubclient (no licence) #include #define DEBUG //#define TLS #define MQTT_VERSION MQTT_VERSION_3_1_1 // Wi-Fi: Access Point SSID and password const char* AP_SSID = "[Redacted]"; const char* AP_PASSWORD = "[Redacted]"; // MQTT: client ID, broker IP address, port, username & password const char* MQTT_CLIENT_ID = "livingroom"; const char* MQTT_SERVER_IP = "192.168.1.10"; #ifdef TLS const uint16_t MQTT_SERVER_PORT = 8883; #else const uint16_t MQTT_SERVER_PORT = 1883; #endif const char* MQTT_USERNAME = "livingroom"; const char* MQTT_PASSWORD = "[Redacted]"; // MQTT: topics // lamp 3 const char* TOPIC_LIGHT1_STATUS = "livingroom/light1/status"; const char* TOPIC_LIGHT1_BRIGHTNESS_STATUS = "livingroom/light1/brightness/status"; const char* TOPIC_LIGHT1_COLOR_STATUS = "livingroom/light1/color/status"; const char* TOPIC_LIGHT1_COMMAND = "livingroom/light1/switch"; const char* TOPIC_LIGHT1_BRIGHTNESS_COMMAND = "livingroom/light1/brightness/set"; const char* TOPIC_LIGHT1_COLOR_COMMAND = "livingroom/light1/color/set"; // lamp 4 const char* TOPIC_LIGHT2_STATUS = "livingroom/light2/status"; const char* TOPIC_LIGHT2_COMMAND = "livingroom/light2/switch"; // brightness sensor const char* TOPIC_TV_BRIGHTNESS_STATUS = "livingroom/tv/status"; // MQTT: payloads // Lamps 3 & 4: "ON"/"OFF" const char* PAYLOAD_ON = "ON"; const char* PAYLOAD_OFF = "OFF"; boolean g_light1_status = false; // turn off by default uint8_t g_light1_brightness = 255; // max value by default uint8_t g_light1_color_red = 255; uint8_t g_light1_color_green = 255; uint8_t g_light1_color_blue = 255; boolean g_light2_status = false; uint16_t g_photocell_value = 0; boolean g_photocell_status = false; // the TV is off uint32_t g_photocell_last_change = 0; boolean g_photocell_status_to_publish = false; // TV const uint16_t PHOTOCELL_THRESHOLD = 150; const uint16_t PHOTOCELL_TIME_OFFSET = 3000; // 3 secondes in the same state before a notification to the controller // buffer used to send/receive data with MQTT const uint8_t MSG_BUFFER_SIZE = 20; char g_msg_buffer[MSG_BUFFER_SIZE]; const uint8_t LIGHT1_RED_PIN = D3; const uint8_t LIGHT1_GREEN_PIN = D2; const uint8_t LIGHT1_BLUE_PIN = D1; const uint8_t LIGHT2_PIN = D5; // D4 seems to be also connected to a internal LED... const uint8_t PHOTOCELL_PIN = A0; // TLS: The fingerprint of the MQTT broker certificate (SHA1) #ifdef TLS // openssl x509 -fingerprint -in .crt const char* CA_FINGERPRINT = "[Redacted]"; // openssl x509 -subject -in .crt const char* CA_SUBJECT = "[Redacted]"; #endif // Fixed IP address: IP address, IP gateway, subnet, dns const IPAddress IP (192, 168, 1, 101); const IPAddress IP_GATEWAY (192, 168, 1, 1); const IPAddress IP_SUBNET (255, 255, 255, 0); const IPAddress IP_DNS (192, 168, 1, 1); // OTA: Hostname (MQTT_CLIENT_ID) & password const char* OTA_PASSWORD = "[Redacted]"; // WiFiFlientSecure instead of WiFiClient, for SSL/TLS support #ifdef TLS WiFiClientSecure g_wifiClient; #else WiFiClient g_wifiClient; #endif PubSubClient g_mqttClient(g_wifiClient); /////////////////////////////////////////////////////////////////////////// // // LIGHT 1 (RGB) // /////////////////////////////////////////////////////////////////////////// /* Function called to publish the status of the light 1 */ void publishLight1Status() { if (g_light1_status) { if (g_mqttClient.publish(TOPIC_LIGHT1_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_LIGHT1_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /* Function called to publish the brightness status of the light 1 */ void publishLight1BrightnessStatus() { snprintf(g_msg_buffer, MSG_BUFFER_SIZE, "%d", g_light1_brightness); if (g_mqttClient.publish(TOPIC_LIGHT1_BRIGHTNESS_STATUS, g_msg_buffer, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_BRIGHTNESS_STATUS); Serial.print(F(". Payload: ")); Serial.println(g_msg_buffer); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } /* Function called to publish the color status of the light 1 */ void publishLight1ColorStatus() { snprintf(g_msg_buffer, MSG_BUFFER_SIZE, "%d,%d,%d", g_light1_color_red, g_light1_color_green, g_light1_color_blue); if (g_mqttClient.publish(TOPIC_LIGHT1_COLOR_STATUS, g_msg_buffer, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT1_COLOR_STATUS); Serial.print(F(". Payload: ")); Serial.println(g_msg_buffer); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } /* Function called to set the color of the light 1 @param p_light1_color_red The value for the red channel @param p_light1_color_green The value for the green channel @param p_light1_color_blue The value for the blue channel */ void setLight1Color(uint8_t p_light1_color_red, uint8_t p_light1_color_green, uint8_t p_light1_color_blue) { analogWrite(LIGHT1_RED_PIN, map(p_light1_color_red, 0, 255, 0, g_light1_brightness)); analogWrite(LIGHT1_GREEN_PIN, map(p_light1_color_green, 0, 255, 0, g_light1_brightness)); analogWrite(LIGHT1_BLUE_PIN, map(p_light1_color_blue, 0, 255, 0, g_light1_brightness)); } /////////////////////////////////////////////////////////////////////////// // // LIGHT 2 // /////////////////////////////////////////////////////////////////////////// /* Function called to publish the status of the light 2 */ void publishLight2Status() { if (g_light2_status) { if (g_mqttClient.publish(TOPIC_LIGHT2_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT2_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_LIGHT2_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_LIGHT2_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /* Function called to switch the status of the light 2 */ void setLight2Status() { if (g_light2_status) { digitalWrite(LIGHT2_PIN, HIGH); } else { digitalWrite(LIGHT2_PIN, LOW); } } /////////////////////////////////////////////////////////////////////////// // // TV BRIGHTNESS SENSOR // /////////////////////////////////////////////////////////////////////////// /* Function called to read the photocell attached to the TV */ void readTVBrightness() { // read the value from the photocell g_photocell_value = analogRead(PHOTOCELL_PIN); if (g_photocell_value > PHOTOCELL_THRESHOLD) { // if the TV was alread switched on... if (g_photocell_status) { // test if it was switched on for the specified duration if (millis() > g_photocell_last_change + PHOTOCELL_TIME_OFFSET) { if (g_photocell_status_to_publish) { g_photocell_status_to_publish = false; publishTVStatus(); } } } else { g_photocell_last_change = millis(); g_photocell_status = true; g_photocell_status_to_publish = true; } } else { if (g_photocell_status) { g_photocell_last_change = millis(); g_photocell_status = false; g_photocell_status_to_publish = true; } else { if (millis() > g_photocell_last_change + PHOTOCELL_TIME_OFFSET) { if (g_photocell_status_to_publish) { g_photocell_status_to_publish = false; publishTVStatus(); } } } } #ifdef DEBUG Serial.print(F("INFO: Photocell read value: ")); Serial.println(g_photocell_value); #endif } /* Function called to publish the status of the TV */ void publishTVStatus() { if (g_photocell_status) { if (g_mqttClient.publish(TOPIC_TV_BRIGHTNESS_STATUS, PAYLOAD_ON, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_TV_BRIGHTNESS_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_ON); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } else { if (g_mqttClient.publish(TOPIC_TV_BRIGHTNESS_STATUS, PAYLOAD_OFF, true)) { #ifdef DEBUG Serial.print(F("INFO: MQTT message publish succeeded. Topic: ")); Serial.print(TOPIC_TV_BRIGHTNESS_STATUS); Serial.print(F(". Payload: ")); Serial.println(PAYLOAD_OFF); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: MQTT message publish failed, either connection lost, or message too large")); #endif } } } /////////////////////////////////////////////////////////////////////////// // // WIFI and TLS // /////////////////////////////////////////////////////////////////////////// /* Function called to setup the connection to the Wi-Fi Access Point */ void setupWifi() { delay(10); // attempt to connect to the Wi-Fi AP WiFi.mode(WIFI_STA); WiFi.begin(AP_SSID, AP_PASSWORD); // define the fixed IP address WiFi.config(IP, IP_GATEWAY, IP_SUBNET, IP_DNS); while (WiFi.status() != WL_CONNECTED) { delay(500); } #ifdef DEBUG Serial.println(F("INFO: Client is now connected to the Wi-Fi AP")); Serial.print(F("INFO: IP address: ")); Serial.println(WiFi.localIP()); #endif #ifdef TLS verifyFingerprint(); #endif } /* Function called to verify the fingerprint of the MQTT server and establish a secure connection */ #ifdef TLS void verifyFingerprint() { if (!g_wifiClient.connect(MQTT_SERVER_IP, MQTT_SERVER_PORT)) { #ifdef DEBUG Serial.println(F("ERROR: The connection failed to the secure MQTT server")); #endif return; } if (g_wifiClient.verify(CA_FINGERPRINT, CA_SUBJECT)) { #ifdef DEBUG Serial.println(F("INFO: The connection is secure")); #endif } else { #ifdef DEBUG Serial.println(F("ERROR: The given certificate does't match")); #endif } } #endif /////////////////////////////////////////////////////////////////////////// // // MQTT // /////////////////////////////////////////////////////////////////////////// /* Function called when a MQTT message 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 callback(char* p_topic, byte* p_payload, unsigned int p_length) { #ifdef DEBUG Serial.println(F("INFO: A new MQTT message arrived")); Serial.print(F("INFO: Topic: ")); Serial.println(p_topic); Serial.print(F("INFO: Payload: ")); for (int i = 0; i < p_length; i++) { Serial.print((char)p_payload[i]); } Serial.println(); Serial.print(F("INFO: Length: ")); Serial.println(p_length); #endif // handle the MQTT topic of the received message if (String(TOPIC_LIGHT1_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } if (payload.equals(String(PAYLOAD_ON))) { if (g_light1_status != true) { g_light1_status = true; setLight1Color(g_light1_color_red, g_light1_color_green, g_light1_color_blue); publishLight1Status(); } } else if (payload.equals(String(PAYLOAD_OFF))) { if (g_light1_status != false) { g_light1_status = false; setLight1Color(LOW, LOW, LOW); // switch off the light publishLight1Status(); } } } else if (String(TOPIC_LIGHT1_BRIGHTNESS_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } uint8_t brightness = payload.toInt(); if (brightness < 0 || brightness > 255) { // do nothing... return; } else { g_light1_brightness = brightness; setLight1Color(g_light1_color_red, g_light1_color_green, g_light1_color_blue); publishLight1BrightnessStatus(); } } else if (String(TOPIC_LIGHT1_COLOR_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } // get the position of the first and second comma uint8_t firstIndex = payload.indexOf(','); uint8_t lastIndex = payload.lastIndexOf(','); // get the value for the red color uint8_t red_color = payload.substring(0, firstIndex).toInt(); if (red_color < 0 || red_color > 255) { return; } else { g_light1_color_red = red_color; } // get the value for the green color uint8_t green_color = payload.substring(firstIndex + 1, lastIndex).toInt(); if (green_color < 0 || green_color > 255) { return; } else { g_light1_color_green = green_color; } // get the value for the blue color uint8_t blue_color = payload.substring(lastIndex + 1).toInt(); if (blue_color < 0 || blue_color > 255) { return; } else { g_light1_color_blue = blue_color; } setLight1Color(g_light1_color_red, g_light1_color_green, g_light1_color_blue); publishLight1ColorStatus(); } else if (String(TOPIC_LIGHT2_COMMAND).equals(p_topic)) { // concat the payload into a string String payload; for (uint8_t i = 0; i < p_length; i++) { payload.concat((char)p_payload[i]); } if (payload.equals(String(PAYLOAD_ON))) { g_light2_status = true; setLight2Status(); publishLight2Status(); } else if (payload.equals(String(PAYLOAD_OFF))) { g_light2_status = false; setLight2Status(); publishLight2Status(); } else { #ifdef DEBUG Serial.println(F("ERROR: The payload of the MQTT message is not valid")); #endif } } else { // do nothing..... #ifdef DEBUG Serial.println(F("INFO: The received MQTT message was not used")); #endif } } /* Function called to reconnect the client to the MQTT broker and publish/subscribe to/from some MQTT topics */ void reconnect() { while (!g_mqttClient.connected()) { if (g_mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) { #ifdef DEBUG Serial.println(F("INFO: The client is successfully connected to the MQTT broker")); #endif // subscribe to the light1 command topic if (g_mqttClient.subscribe(TOPIC_LIGHT1_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT1_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT1_COMMAND); #endif } // subscribe to the light1 brightness command topic if (g_mqttClient.subscribe(TOPIC_LIGHT1_BRIGHTNESS_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT1_BRIGHTNESS_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT1_BRIGHTNESS_COMMAND); #endif } // subscribe to the light1 brightness command topic if (g_mqttClient.subscribe(TOPIC_LIGHT1_COLOR_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT1_COLOR_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT1_COLOR_COMMAND); #endif } // subscribe to the light2 command topic if (g_mqttClient.subscribe(TOPIC_LIGHT2_COMMAND)) { #ifdef DEBUG Serial.print(F("INFO: Sending the MQTT subscribe succeeded. Topic: ")); Serial.println(TOPIC_LIGHT2_COMMAND); #endif } else { #ifdef DEBUG Serial.print(F("ERROR: Sending the MQTT subscribe failed. Topic: ")); Serial.println(TOPIC_LIGHT2_COMMAND); #endif } // set the initial status of lights 1 & 2 setLight1Color(LOW, LOW, LOW); setLight2Status(); // publish the initial status of lights 1 & 2 publishLight1Status(); publishLight1BrightnessStatus(); publishLight1ColorStatus(); publishLight2Status(); publishTVStatus(); } else { #ifdef DEBUG Serial.println(F("ERROR: The connection failed with the MQTT broker")); Serial.print("ERROR: rc: "); Serial.println(g_mqttClient.state()); // wait 5 seconds before retrying delay(5000); #endif } } } /////////////////////////////////////////////////////////////////////////// // // SETUP and LOOP // /////////////////////////////////////////////////////////////////////////// /* Function called once to initialize the board */ void setup() { #ifdef DEBUG Serial.begin(115200); Serial.println(F("\nINFO: The Wi-Fi module is starting...")); #endif // init the LEDs as output, the TV brightness sensor as input pinMode(LIGHT1_RED_PIN, OUTPUT); pinMode(LIGHT1_GREEN_PIN, OUTPUT); pinMode(LIGHT1_BLUE_PIN, OUTPUT); pinMode(LIGHT2_PIN, OUTPUT); pinMode(PHOTOCELL_PIN, INPUT); setupWifi(); // set the MQTT broker IP address and port g_mqttClient.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT); // set the MQTT callback function g_mqttClient.setCallback(callback); // set the hostname & password for OTA ArduinoOTA.setHostname(MQTT_CLIENT_ID); ArduinoOTA.setPassword(OTA_PASSWORD); #ifdef DEBUG ArduinoOTA.onStart([]() { Serial.println(F("INFO: OTA starts")); }); ArduinoOTA.onEnd([]() { Serial.println(F("INFO: OTA ends")); }); ArduinoOTA.onError([](ota_error_t error) { Serial.println(F("INFO: An error occurs during OTA")); }); #endif ArduinoOTA.begin(); // blink the internal LED to indicate the end of the startup pinMode(BUILTIN_LED, OUTPUT); digitalWrite(BUILTIN_LED, LOW); delay(100); digitalWrite(BUILTIN_LED, HIGH); delay(100); digitalWrite(BUILTIN_LED, LOW); delay(100); digitalWrite(BUILTIN_LED, HIGH); } /* Function called infinitely after the setup function */ void loop() { // keep the MQTT client connected to the broker if (!g_mqttClient.connected()) { reconnect(); } g_mqttClient.loop(); // a delay is necessary if the debug is disabled // without a delay, there is some latency to switch on/off a light yield(); //delay(100); readTVBrightness(); yield(); ArduinoOTA.handle(); yield(); }