Merge branch 'dev' into feature/live-preview-websockets
This commit is contained in:
commit
e9c782bf9e
31
.github/workflows/wled-ci.yml
vendored
Normal file
31
.github/workflows/wled-ci.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: PlatformIO CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
- name: Run PlatformIO
|
||||
run: pio run
|
63
CHANGELOG.md
63
CHANGELOG.md
@ -1,6 +1,65 @@
|
||||
## WLED changelog
|
||||
|
||||
### WLED version 0.11.0
|
||||
### Development versions after 0.11.1 release
|
||||
|
||||
#### Build 2012210
|
||||
|
||||
- Split index.htm in separate CSS + JS files (PR #1542)
|
||||
- Minify UI HTML, saving >1.5kB flash
|
||||
- Fixed JShint warnings
|
||||
|
||||
#### Build 2012180
|
||||
|
||||
- Boot brightness 0 will now use the brightness from preset
|
||||
- Add iOS scrolling momentum (from PR #1528)
|
||||
|
||||
### WLED release 0.11.1
|
||||
|
||||
#### Build 2012180
|
||||
|
||||
- Release of WLED 0.11.1 "Mirai"
|
||||
- Fixed AP hide not saving (fixes #1520)
|
||||
- Fixed MQTT password re-transmitted to HTML
|
||||
- Hide Update buttons while uploading, accept .bin
|
||||
- Make sure AP password is at least 8 characters long
|
||||
|
||||
### Development versions after 0.11.0 release
|
||||
|
||||
#### Build 2012160
|
||||
|
||||
- Bump Espalexa to 2.5.0, fixing discovery (PR Espalexa/#152, originally PR #1497)
|
||||
|
||||
#### Build 2012150
|
||||
|
||||
- Added Blends FX (PR #1491)
|
||||
- Fixed an issue that made it impossible to deactivate timed presets
|
||||
|
||||
#### Build 2012140
|
||||
|
||||
- Added Preset ID quick display option (PR #1462)
|
||||
- Fixed LEDs not turning on when using gamma correct brightness and LEDPIN 2 (default)
|
||||
- Fixed notifier applying main segment to selected segments on notification with FX/Col disabled
|
||||
|
||||
#### Build 2012130
|
||||
|
||||
- Fixed RGBW mode not saved between reboots (fixes #1457)
|
||||
- Added brightness scaling in palette function for default (PR #1484)
|
||||
|
||||
#### Build 2012101
|
||||
|
||||
- Fixed preset cycle default duration rounded down to nearest 10sec interval (#1458)
|
||||
- Enabled E1.31/DDP/Art-Net in AP mode
|
||||
|
||||
#### Build 2012100
|
||||
|
||||
- Fixed multi-segment preset cycle
|
||||
- Fixed EEPROM (pre-0.11 settings) not cleared on factory reset
|
||||
- Fixed an issue with intermittent crashes on FX change (PR #1465)
|
||||
- Added function to know if strip is updating (PR #1466)
|
||||
- Fixed using colorwheel sliding the UI (PR #1459)
|
||||
- Fixed analog clock settings not saving (PR #1448)
|
||||
- Added Temperature palette (PR #1430)
|
||||
- Added Candy cane FX (PR #1445)
|
||||
|
||||
#### Build 2012020
|
||||
|
||||
@ -11,6 +70,8 @@
|
||||
|
||||
- Fixed compilation for analog (PWM) LEDs
|
||||
|
||||
### WLED version 0.11.0
|
||||
|
||||
#### Build 2011290
|
||||
|
||||
- Release of WLED 0.11.0 "Mirai"
|
||||
|
5
images/Readme.md
Normal file
5
images/Readme.md
Normal file
@ -0,0 +1,5 @@
|
||||
### Additional Logos
|
||||
|
||||
Additional awesome logos for WLED can be found here [Aircoookie/Akemi](https://github.com/Aircoookie/Akemi).
|
||||
|
||||
<img src="https://github.com/Aircoookie/Akemi/blob/master/akemi/001_cheerful.png">
|
8
package-lock.json
generated
8
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.10.2",
|
||||
"version": "0.11.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -958,9 +958,9 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
},
|
||||
"inliner": {
|
||||
"version": "1.13.1",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.11.0",
|
||||
"version": "0.11.1",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
|
@ -179,7 +179,10 @@ extra_scripts = pio/name-firmware.py
|
||||
framework = arduino
|
||||
board_build.flash_mode = dout
|
||||
monitor_speed = 115200
|
||||
# slow upload speed (comment this out with a ';' when building for development use)
|
||||
upload_speed = 115200
|
||||
# fast upload speed (remove ';' when building for development use)
|
||||
; upload_speed = 921600
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# LIBRARIES: required dependencies
|
||||
@ -361,6 +364,14 @@ board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# custom board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -69,6 +69,8 @@ function writeHtmlGzipped(sourceFile, resultFile) {
|
||||
console.info("Reading " + sourceFile);
|
||||
new inliner(sourceFile, function (error, html) {
|
||||
console.info("Inlined " + html.length + " characters");
|
||||
html = filter(html, "html-minify-ui");
|
||||
console.info("Minified to " + html.length + " characters");
|
||||
|
||||
if (error) {
|
||||
console.warn(error);
|
||||
@ -123,6 +125,16 @@ function filter(str, type) {
|
||||
continueOnParseError: false,
|
||||
removeComments: true,
|
||||
});
|
||||
} else if (type == "html-minify-ui") {
|
||||
return MinifyHTML(str, {
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
maxLineLength: 80,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
continueOnParseError: false,
|
||||
removeComments: true,
|
||||
});
|
||||
} else {
|
||||
console.warn("Unknown filter: " + type);
|
||||
return str;
|
||||
@ -132,7 +144,7 @@ function filter(str, type) {
|
||||
function specToChunk(srcDir, s) {
|
||||
if (s.method == "plaintext") {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
const str = buf.toString("ascii");
|
||||
const str = buf.toString("utf-8");
|
||||
const chunk = `
|
||||
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
||||
const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${filter(str, s.filter)}${
|
||||
|
40
usermods/BME280_v2/README.md
Normal file
40
usermods/BME280_v2/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield.
|
||||
|
||||
- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
|
||||
- Data is published over MQTT so make sure you've enabled the MQTT sync interface.
|
||||
- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages!
|
||||
|
||||
To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`)
|
||||
```ini
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_BME280
|
||||
```
|
||||
or define `USERMOD_BME280` in `my_config.h`
|
||||
```c++
|
||||
#define USERMOD_BME280
|
||||
```
|
||||
|
||||
Changes include:
|
||||
- Adjustable measure intervals
|
||||
- Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude
|
||||
- Adjustment of number of decimal places in published sensor values
|
||||
- Separate adjustment for temperature, humidity and pressure values
|
||||
- Values are rounded to the specified number of decimal places
|
||||
- Pressure measured in units of hPa instead of Pa
|
||||
- Calculation of heat index (apparent temperature) and dew point
|
||||
- These, along with humidity measurements, are disabled if the sensor is a BMP280
|
||||
- 16x oversampling of sensor during measurement
|
||||
- Values are only published if they are different from the previous value
|
||||
- Values are published on startup (continually until the MQTT broker acknowledges a successful publication)
|
||||
|
||||
Adjustments are made through preprocessor definitions at the start of the class definition.
|
||||
|
||||
MQTT topics are as follows:
|
||||
Measurement type | MQTT topic
|
||||
--- | ---
|
||||
Temperature | `<deviceTopic>/temperature`
|
||||
Humidity | `<deviceTopic>/humidity`
|
||||
Pressure | `<deviceTopic>/pressure`
|
||||
Heat index | `<deviceTopic>/heat_index`
|
||||
Dew point | `<deviceTopic>/dew_point`
|
212
usermods/BME280_v2/usermod_bme280.h
Normal file
212
usermods/BME280_v2/usermod_bme280.h
Normal file
@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <BME280I2C.h> // BME280 sensor
|
||||
#include <EnvironmentCalculations.h> // BME280 extended measurements
|
||||
|
||||
class UsermodBME280 : public Usermod
|
||||
{
|
||||
private:
|
||||
// User-defined configuration
|
||||
#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit
|
||||
#define TemperatureDecimals 1 // Number of decimal places in published temperaure values
|
||||
#define HumidityDecimals 0 // Number of decimal places in published humidity values
|
||||
#define PressureDecimals 2 // Number of decimal places in published pressure values
|
||||
#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds
|
||||
#define PressureInterval 300 // Interval to measure pressure in seconds
|
||||
|
||||
// Sanity checks
|
||||
#if !defined(TemperatureDecimals) || TemperatureDecimals < 0
|
||||
#define TemperatureDecimals 0
|
||||
#endif
|
||||
#if !defined(HumidityDecimals) || HumidityDecimals < 0
|
||||
#define HumidityDecimals 0
|
||||
#endif
|
||||
#if !defined(PressureDecimals) || PressureDecimals < 0
|
||||
#define PressureDecimals 0
|
||||
#endif
|
||||
#if !defined(TemperatureInterval) || TemperatureInterval < 0
|
||||
#define TemperatureInterval 1
|
||||
#endif
|
||||
#if !defined(PressureInterval) || PressureInterval < 0
|
||||
#define PressureInterval TemperatureInterval
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
|
||||
uint8_t SCL_PIN = 22;
|
||||
uint8_t SDA_PIN = 21;
|
||||
#else // ESP8266 boards
|
||||
uint8_t SCL_PIN = 5;
|
||||
uint8_t SDA_PIN = 4;
|
||||
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
|
||||
#endif
|
||||
|
||||
// BME280 sensor settings
|
||||
BME280I2C::Settings settings{
|
||||
BME280::OSR_X16, // Temperature oversampling x16
|
||||
BME280::OSR_X16, // Humidity oversampling x16
|
||||
BME280::OSR_X16, // Pressure oversampling x16
|
||||
// Defaults
|
||||
BME280::Mode_Forced,
|
||||
BME280::StandbyTime_1000ms,
|
||||
BME280::Filter_Off,
|
||||
BME280::SpiEnable_False,
|
||||
BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76
|
||||
};
|
||||
|
||||
BME280I2C bme{settings};
|
||||
|
||||
uint8_t SensorType;
|
||||
|
||||
// Measurement timers
|
||||
long timer;
|
||||
long lastTemperatureMeasure = 0;
|
||||
long lastPressureMeasure = 0;
|
||||
|
||||
// Current sensor values
|
||||
float SensorTemperature;
|
||||
float SensorHumidity;
|
||||
float SensorHeatIndex;
|
||||
float SensorDewPoint;
|
||||
float SensorPressure;
|
||||
// Track previous sensor values
|
||||
float lastTemperature;
|
||||
float lastHumidity;
|
||||
float lastHeatIndex;
|
||||
float lastDewPoint;
|
||||
float lastPressure;
|
||||
|
||||
// Store packet IDs of MQTT publications
|
||||
uint16_t mqttTemperaturePub = 0;
|
||||
uint16_t mqttPressurePub = 0;
|
||||
|
||||
void UpdateBME280Data(int SensorType)
|
||||
{
|
||||
float _temperature, _humidity, _pressure;
|
||||
#ifdef Celsius
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
|
||||
#else
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit);
|
||||
#endif
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
|
||||
|
||||
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
|
||||
|
||||
SensorTemperature = _temperature;
|
||||
SensorHumidity = _humidity;
|
||||
SensorPressure = _pressure;
|
||||
if (SensorType == 1)
|
||||
{
|
||||
SensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
|
||||
SensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(SDA_PIN, SCL_PIN);
|
||||
|
||||
if (!bme.begin())
|
||||
{
|
||||
SensorType = 0;
|
||||
Serial.println("Could not find BME280I2C sensor!");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (bme.chipModel())
|
||||
{
|
||||
case BME280::ChipModel_BME280:
|
||||
SensorType = 1;
|
||||
Serial.println("Found BME280 sensor! Success.");
|
||||
break;
|
||||
case BME280::ChipModel_BMP280:
|
||||
SensorType = 2;
|
||||
Serial.println("Found BMP280 sensor! No Humidity available.");
|
||||
break;
|
||||
default:
|
||||
SensorType = 0;
|
||||
Serial.println("Found UNKNOWN sensor! Error!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// BME280 sensor MQTT publishing
|
||||
// Check if sensor present and MQTT Connected, otherwise it will crash the MCU
|
||||
if (SensorType != 0 && mqtt != nullptr)
|
||||
{
|
||||
// Timer to fetch new temperature, humidity and pressure data at intervals
|
||||
timer = millis();
|
||||
|
||||
if (timer - lastTemperatureMeasure >= TemperatureInterval * 1000 || mqttTemperaturePub == 0)
|
||||
{
|
||||
lastTemperatureMeasure = timer;
|
||||
|
||||
UpdateBME280Data(SensorType);
|
||||
|
||||
float Temperature = roundf(SensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
float Humidity, HeatIndex, DewPoint;
|
||||
|
||||
// If temperature has changed since last measure, create string populated with device topic
|
||||
// from the UI and values read from sensor, then publish to broker
|
||||
if (Temperature != lastTemperature)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/temperature";
|
||||
mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(Temperature, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastTemperature = Temperature; // Update last sensor temperature for next loop
|
||||
|
||||
if (SensorType == 1) // Only if sensor is a BME280
|
||||
{
|
||||
Humidity = roundf(SensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals);
|
||||
HeatIndex = roundf(SensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
DewPoint = roundf(SensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals);
|
||||
|
||||
if (Humidity != lastHumidity)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/humidity";
|
||||
mqtt->publish(topic.c_str(), 0, false, String(Humidity, HumidityDecimals).c_str());
|
||||
}
|
||||
|
||||
if (HeatIndex != lastHeatIndex)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/heat_index";
|
||||
mqtt->publish(topic.c_str(), 0, false, String(HeatIndex, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
if (DewPoint != lastDewPoint)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/dew_point";
|
||||
mqtt->publish(topic.c_str(), 0, false, String(DewPoint, TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastHumidity = Humidity;
|
||||
lastHeatIndex = HeatIndex;
|
||||
lastDewPoint = DewPoint;
|
||||
}
|
||||
}
|
||||
|
||||
if (timer - lastPressureMeasure >= PressureInterval * 1000 || mqttPressurePub == 0)
|
||||
{
|
||||
lastPressureMeasure = timer;
|
||||
|
||||
float Pressure = roundf(SensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals);
|
||||
|
||||
if (Pressure != lastPressure)
|
||||
{
|
||||
String topic = String(mqttDeviceTopic) + "/pressure";
|
||||
mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(Pressure, PressureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastPressure = Pressure;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@ -1,17 +1,32 @@
|
||||
# Fix unreachable net services V2
|
||||
|
||||
**Attention: This usermod compiles only for ESP8266**
|
||||
|
||||
This usermod-v2 modification performs a ping request to the local IP address every 60 seconds. By this procedure the net services of WLED remains accessible in some problematic WLAN environments.
|
||||
|
||||
The modification works with static or DHCP IP address configuration.
|
||||
|
||||
**Webinterface**: The number of pings and reconnects is displayed on the info page in the web interface.
|
||||
|
||||
_Story:_
|
||||
|
||||
Unfortunately, with all ESP projects where a web server or other network services are running, I have the problem that after some time the web server is no longer accessible. Now I found out that the connection is at least reestablished when a ping request is executed by the device.
|
||||
|
||||
With this modification, in the worst case, the network functions are not available for 60 seconds until the next ping request.
|
||||
|
||||
## Webinterface
|
||||
|
||||
The number of pings and reconnects is displayed on the info page in the web interface.
|
||||
The ping delay can be changed. Changes persist after a reboot.
|
||||
|
||||
## JSON API
|
||||
|
||||
The usermod supports the following state changes:
|
||||
|
||||
| JSON key | Value range | Description |
|
||||
|-------------|------------------|---------------------------------|
|
||||
| PingDelayMs | 5000 to 18000000 | Deactivdate/activate the sensor |
|
||||
|
||||
Changes also persist after a reboot.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy the file `usermod_Fix_unreachable_netservices.h` to the `wled00` directory.
|
||||
|
@ -1,6 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#if defined(ESP32)
|
||||
#warning "Usermod FixUnreachableNetServices works only with ESP8266 builds"
|
||||
class FixUnreachableNetServices : public Usermod
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ping.h>
|
||||
|
||||
/*
|
||||
@ -23,34 +31,37 @@
|
||||
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
*/
|
||||
|
||||
class FixUnreachableNetServices : public Usermod {
|
||||
private:
|
||||
class FixUnreachableNetServices : public Usermod
|
||||
{
|
||||
private:
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long m_lastTime = 0;
|
||||
|
||||
// desclare required variables
|
||||
const unsigned int PingDelayMs = 60000;
|
||||
// declare required variables
|
||||
unsigned long m_pingDelayMs = 60000;
|
||||
unsigned long m_connectedWiFi = 0;
|
||||
ping_option m_pingOpt;
|
||||
unsigned int m_pingCount = 0;
|
||||
bool m_updateConfig = false;
|
||||
|
||||
public:
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
/**
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup() {
|
||||
void setup()
|
||||
{
|
||||
//Serial.println("Hello from my usermod!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected() {
|
||||
void connected()
|
||||
{
|
||||
//Serial.println("Connected to WiFi!");
|
||||
|
||||
++m_connectedWiFi;
|
||||
@ -59,42 +70,44 @@ class FixUnreachableNetServices : public Usermod {
|
||||
memset(&m_pingOpt, 0, sizeof(struct ping_option));
|
||||
m_pingOpt.count = 1;
|
||||
m_pingOpt.ip = WiFi.localIP();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*
|
||||
* Tips:
|
||||
* 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
|
||||
* Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
|
||||
*
|
||||
* 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
|
||||
* Instead, use a timer check as shown here.
|
||||
/**
|
||||
* loop
|
||||
*/
|
||||
void loop() {
|
||||
if (m_connectedWiFi > 0 && millis()-m_lastTime > PingDelayMs)
|
||||
void loop()
|
||||
{
|
||||
if (m_connectedWiFi > 0 && millis() - m_lastTime > m_pingDelayMs)
|
||||
{
|
||||
ping_start(&m_pingOpt);
|
||||
m_lastTime = millis();
|
||||
++m_pingCount;
|
||||
}
|
||||
if (m_updateConfig)
|
||||
{
|
||||
serializeConfig();
|
||||
m_updateConfig = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
//this code adds "u":{"⚡ Ping fix pings": m_pingCount} to the info object
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
JsonArray infoArr = user.createNestedArray("⚡ Ping fix pings"); //name
|
||||
String uiDomString = "⚡ Ping fix pings<span style=\"display:block;padding-left:25px;\">\
|
||||
Delay <input type=\"number\" min=\"5\" max=\"300\" value=\"";
|
||||
uiDomString += (unsigned long)(m_pingDelayMs / 1000);
|
||||
uiDomString += "\" onchange=\"requestJson({PingDelay:parseInt(this.value)});\">sec</span>";
|
||||
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); //name
|
||||
infoArr.add(m_pingCount); //value
|
||||
|
||||
//this code adds "u":{"⚡ Reconnects": m_connectedWiFi - 1} to the info object
|
||||
@ -102,29 +115,48 @@ class FixUnreachableNetServices : public Usermod {
|
||||
infoArr.add(m_connectedWiFi - 1); //value
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void addToJsonState(JsonObject& root)
|
||||
void addToJsonState(JsonObject &root)
|
||||
{
|
||||
//root["user0"] = userVar0;
|
||||
root["PingDelay"] = (m_pingDelayMs/1000);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root)
|
||||
void readFromJsonState(JsonObject &root)
|
||||
{
|
||||
//userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
|
||||
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
|
||||
if (root["PingDelay"] != nullptr)
|
||||
{
|
||||
m_pingDelayMs = (1000 * max(1UL, min(300UL, root["PingDelay"].as<unsigned long>())));
|
||||
m_updateConfig = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* provide the changeable values
|
||||
*/
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("FixUnreachableNetServices");
|
||||
top["PingDelayMs"] = m_pingDelayMs;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* restore the changeable values
|
||||
*/
|
||||
void readFromConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root["FixUnreachableNetServices"];
|
||||
m_pingDelayMs = top["PingDelayMs"] | m_pingDelayMs;
|
||||
m_pingDelayMs = max(5000UL, min(18000000UL, m_pingDelayMs));
|
||||
}
|
||||
|
||||
/**
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
@ -132,7 +164,5 @@ class FixUnreachableNetServices : public Usermod {
|
||||
{
|
||||
return USERMOD_ID_FIXNETSERVICES;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
||||
#endif
|
||||
|
@ -1,17 +0,0 @@
|
||||
# Fix unreachable Webserver
|
||||
|
||||
This modification performs a ping request to the local IP address every 60 seconds. By this procedure the web server remains accessible in some problematic WLAN environments.
|
||||
|
||||
The modification works with static or DHCP IP address configuration
|
||||
|
||||
_Story:_
|
||||
|
||||
Unfortunately, with all ESP projects where a web server or other network services are running, I have the problem that after some time the web server is no longer accessible. Now I found out that the connection is at least reestablished when a ping request is executed by the device.
|
||||
|
||||
With this modification, in the worst case, the network functions are not available for 60 seconds until the next ping request.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy and replace the file `usermod.cpp` in wled00 directory.
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
#include "wled.h"
|
||||
/*
|
||||
* This file allows you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
|
||||
* bytes 2400+ are currently ununsed, but might be used for future wled features
|
||||
*/
|
||||
|
||||
#include <ping.h>
|
||||
|
||||
const int PingDelayMs = 60000;
|
||||
long lastCheckTime = 0;
|
||||
bool connectedWiFi = false;
|
||||
ping_option pingOpt;
|
||||
|
||||
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
|
||||
|
||||
//gets called once at boot. Do all initialization that doesn't depend on network here
|
||||
void userSetup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
|
||||
void userConnected()
|
||||
{
|
||||
connectedWiFi = true;
|
||||
// initialize ping_options structure
|
||||
memset(&pingOpt, 0, sizeof(struct ping_option));
|
||||
pingOpt.count = 1;
|
||||
pingOpt.ip = WiFi.localIP();
|
||||
}
|
||||
|
||||
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
|
||||
void userLoop()
|
||||
{
|
||||
if (connectedWiFi && millis()-lastCheckTime > PingDelayMs)
|
||||
{
|
||||
ping_start(&pingOpt);
|
||||
lastCheckTime = millis();
|
||||
}
|
||||
}
|
@ -11,8 +11,8 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
|
||||
|
||||
The info page in the web interface shows the items below
|
||||
|
||||
- the state of the sensor. By clicking on the state the sensor can be deactivated/activated.
|
||||
**I recommend to deactivate the sensor before installing an OTA update**.
|
||||
- the state of the sensor. By clicking on the state the sensor can be deactivated/activated. Changes persist after a reboot.
|
||||
**I recommend to deactivate the sensor before an OTA update and activate it again afterwards**.
|
||||
- the remaining time of the off timer.
|
||||
|
||||
## JSON API
|
||||
@ -24,6 +24,8 @@ The usermod supports the following state changes:
|
||||
| PIRenabled | bool | Deactivdate/activate the sensor |
|
||||
| PIRoffSec | 60 to 43200 | Off timer seconds |
|
||||
|
||||
Changes also persist after a reboot.
|
||||
|
||||
## Sensor connection
|
||||
|
||||
My setup uses an HC-SR501 sensor, a HC-SR505 should also work.
|
||||
@ -72,26 +74,36 @@ void registerUsermods()
|
||||
}
|
||||
```
|
||||
|
||||
## Usermod installation (advanced mode)
|
||||
## API to enable/disable the PIR sensor from outside. For example from another usermod.
|
||||
|
||||
In this mode IR sensor will disable PIR when light ON by remote controller and enable PIR when light OFF.
|
||||
The class provides the static method `PIRsensorSwitch* PIRsensorSwitch::GetInstance()` to get a pointer to the usermod object.
|
||||
|
||||
1. Copy the file `usermod_PIR_sensor_switch.h` to the `wled00` directory.
|
||||
2. Register the usermod by adding `#include "usermod_PIR_sensor_switch.h"` in the top and `registerUsermod(new PIRsensorSwitch());` in the bottom of `usermods_list.cpp`.
|
||||
3. Add to the line 237, on `wled.h` in the `wled00` directory:
|
||||
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
|
||||
|
||||
`WLED_GLOBAL bool m_PIRenabled _INIT(true); // enable PIR sensor`
|
||||
### There are two options to get access to the usermod instance:
|
||||
|
||||
4. On `ir.cpp` in the `wled00` directory, add to the IR controller's mapping (beyond line 200):
|
||||
1. Include `usermod_PIR_sensor_switch.h` **before** you include the other usermod in `usermods_list.cpp'
|
||||
|
||||
- To the off button:
|
||||
`m_PIRenabled = true;`
|
||||
or
|
||||
|
||||
- To the on button:
|
||||
`m_PIRenabled = false;`
|
||||
2. Use `#include "usermod_PIR_sensor_switch.h"` at the top of the `usermod.h` where you need it.
|
||||
|
||||
5. Edit line 40, on `usermod_PIR_sensor_switch.h` in the `wled00` directory:
|
||||
**Example usermod.h :**
|
||||
```cpp
|
||||
#include "wled.h"
|
||||
|
||||
`\\bool m_PIRenabled = true;`
|
||||
#include "usermod_PIR_sensor_switch.h"
|
||||
|
||||
class MyUsermod : public Usermod {
|
||||
//...
|
||||
|
||||
void togglePIRSensor() {
|
||||
if (PIRsensorSwitch::GetInstance() != nullptr) {
|
||||
PIRsensorSwitch::GetInstance()->EnablePIRsensor(!PIRsensorSwitch::GetInstance()->PIRsensorEnabled());
|
||||
}
|
||||
}
|
||||
//...
|
||||
};
|
||||
```
|
||||
|
||||
Have fun - @gegu
|
||||
|
@ -24,8 +24,41 @@
|
||||
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
*/
|
||||
|
||||
class PIRsensorSwitch : public Usermod {
|
||||
private:
|
||||
class PIRsensorSwitch : public Usermod
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
PIRsensorSwitch()
|
||||
{
|
||||
// set static instance pointer
|
||||
PIRsensorSwitchInstance(this);
|
||||
}
|
||||
/**
|
||||
* desctructor
|
||||
*/
|
||||
~PIRsensorSwitch()
|
||||
{
|
||||
PIRsensorSwitchInstance(nullptr, true);
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the instance pointer of the class
|
||||
*/
|
||||
static PIRsensorSwitch *GetInstance() { return PIRsensorSwitchInstance(); }
|
||||
|
||||
/**
|
||||
* Enable/Disable the PIR sensor
|
||||
*/
|
||||
void EnablePIRsensor(bool enable) { m_PIRenabled = enable; }
|
||||
/**
|
||||
* Get PIR sensor enabled/disabled state
|
||||
*/
|
||||
bool PIRsensorEnabled() { return m_PIRenabled; }
|
||||
|
||||
private:
|
||||
// PIR sensor pin
|
||||
const uint8_t PIRsensorPin = 13; // D7 on D1 mini
|
||||
// notification mode for colorUpdated()
|
||||
@ -38,53 +71,59 @@ class PIRsensorSwitch : public Usermod {
|
||||
byte m_PIRsensorPinState = LOW;
|
||||
// PIR sensor enabled - ISR attached
|
||||
bool m_PIRenabled = true;
|
||||
// state if serializeConfig() should be called
|
||||
bool m_updateConfig = false;
|
||||
|
||||
/*
|
||||
/**
|
||||
* return or change if new PIR sensor state is available
|
||||
*/
|
||||
static volatile bool newPIRsensorState(bool changeState = false, bool newState = false) {
|
||||
static volatile bool s_PIRsensorState = false;
|
||||
if (changeState) {
|
||||
s_PIRsensorState = newState;
|
||||
}
|
||||
return s_PIRsensorState;
|
||||
}
|
||||
static volatile bool newPIRsensorState(bool changeState = false, bool newState = false);
|
||||
|
||||
/*
|
||||
/**
|
||||
* PIR sensor state has changed
|
||||
*/
|
||||
static void IRAM_ATTR ISR_PIRstateChange() {
|
||||
newPIRsensorState(true, true);
|
||||
}
|
||||
static void IRAM_ATTR ISR_PIRstateChange();
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set/get instance pointer
|
||||
*/
|
||||
static PIRsensorSwitch *PIRsensorSwitchInstance(PIRsensorSwitch *pInstance = nullptr, bool bRemoveInstance = false);
|
||||
|
||||
/**
|
||||
* switch strip on/off
|
||||
*/
|
||||
void switchStrip(bool switchOn) {
|
||||
if (switchOn && bri == 0) {
|
||||
void switchStrip(bool switchOn)
|
||||
{
|
||||
if (switchOn && bri == 0)
|
||||
{
|
||||
bri = briLast;
|
||||
colorUpdated(NotifyUpdateMode);
|
||||
}
|
||||
else if (!switchOn && bri != 0) {
|
||||
else if (!switchOn && bri != 0)
|
||||
{
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
colorUpdated(NotifyUpdateMode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Read and update PIR sensor state.
|
||||
* Initilize/reset switch off timer
|
||||
*/
|
||||
bool updatePIRsensorState() {
|
||||
if (newPIRsensorState()) {
|
||||
bool updatePIRsensorState()
|
||||
{
|
||||
if (newPIRsensorState())
|
||||
{
|
||||
m_PIRsensorPinState = digitalRead(PIRsensorPin);
|
||||
|
||||
if (m_PIRsensorPinState == HIGH) {
|
||||
if (m_PIRsensorPinState == HIGH)
|
||||
{
|
||||
m_offTimerStart = 0;
|
||||
switchStrip(true);
|
||||
}
|
||||
else if (bri != 0) {
|
||||
else if (bri != 0)
|
||||
{
|
||||
// start switch off timer
|
||||
m_offTimerStart = millis();
|
||||
}
|
||||
@ -94,12 +133,15 @@ class PIRsensorSwitch : public Usermod {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* switch off the strip if the delay has elapsed
|
||||
*/
|
||||
bool handleOffTimer() {
|
||||
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay) {
|
||||
if (m_PIRenabled == true){
|
||||
bool handleOffTimer()
|
||||
{
|
||||
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
|
||||
{
|
||||
if (m_PIRenabled == true)
|
||||
{
|
||||
switchStrip(false);
|
||||
}
|
||||
m_offTimerStart = 0;
|
||||
@ -108,60 +150,73 @@ class PIRsensorSwitch : public Usermod {
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
/**
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup() {
|
||||
void setup()
|
||||
{
|
||||
// PIR Sensor mode INPUT_PULLUP
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
if (m_PIRenabled)
|
||||
{
|
||||
// assign interrupt function and set CHANGE mode
|
||||
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected() {
|
||||
|
||||
void connected()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop() {
|
||||
if (!updatePIRsensorState()) {
|
||||
void loop()
|
||||
{
|
||||
if (!updatePIRsensorState())
|
||||
{
|
||||
handleOffTimer();
|
||||
if (m_updateConfig)
|
||||
{
|
||||
serializeConfig();
|
||||
m_updateConfig = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
*
|
||||
* Add PIR sensor state and switch off timer duration to jsoninfo
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
//this code adds "u":{"⏲ PIR sensor state":uiDomString} to the info object
|
||||
// the value contains a button to toggle the sensor enabled/disabled
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
JsonArray infoArr = user.createNestedArray("⏲ PIR sensor state"); //name
|
||||
String uiDomString = "<button class=\"btn infobtn\" onclick=\"requestJson({PIRenabled:";
|
||||
String sensorStateInfo;
|
||||
|
||||
// PIR sensor state
|
||||
if (m_PIRenabled) {
|
||||
if (m_PIRenabled)
|
||||
{
|
||||
uiDomString += "false";
|
||||
sensorStateInfo = (m_PIRsensorPinState != LOW ? "active" : "inactive"); //value
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uiDomString += "true";
|
||||
sensorStateInfo = "Disabled !";
|
||||
}
|
||||
@ -171,73 +226,108 @@ class PIRsensorSwitch : public Usermod {
|
||||
infoArr.add(uiDomString); //value
|
||||
|
||||
//this code adds "u":{"⏲ switch off timer":uiDomString} to the info object
|
||||
infoArr = user.createNestedArray("⏲ switch off timer"); //name
|
||||
uiDomString = "⏲ switch off timer<span style=\"display:block;padding-left:25px;\">\
|
||||
after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
|
||||
uiDomString += (m_switchOffDelay / 60000);
|
||||
uiDomString += "\" onchange=\"requestJson({PIRoffSec:parseInt(this.value)*60});\">min</span>";
|
||||
infoArr = user.createNestedArray(uiDomString); //name
|
||||
|
||||
// off timer
|
||||
if (m_offTimerStart > 0) {
|
||||
if (m_offTimerStart > 0)
|
||||
{
|
||||
uiDomString = "";
|
||||
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
|
||||
if (offSeconds >= 3600) {
|
||||
if (offSeconds >= 3600)
|
||||
{
|
||||
uiDomString += (offSeconds / 3600);
|
||||
uiDomString += " hours ";
|
||||
offSeconds %= 3600;
|
||||
}
|
||||
if (offSeconds >= 60) {
|
||||
if (offSeconds >= 60)
|
||||
{
|
||||
uiDomString += (offSeconds / 60);
|
||||
offSeconds %= 60;
|
||||
} else if (uiDomString.length() > 0){
|
||||
}
|
||||
else if (uiDomString.length() > 0)
|
||||
{
|
||||
uiDomString += 0;
|
||||
}
|
||||
if (uiDomString.length() > 0){
|
||||
if (uiDomString.length() > 0)
|
||||
{
|
||||
uiDomString += " min ";
|
||||
}
|
||||
uiDomString += (offSeconds);
|
||||
infoArr.add(uiDomString + " sec");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
infoArr.add("inactive");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
* Add "PIRenabled" to json state. This can be used to disable/enable the sensor.
|
||||
* Add "PIRoffSec" to json state. This can be used to adjust <m_switchOffDelay> milliseconds .
|
||||
* Add "PIRoffSec" to json state. This can be used to adjust <m_switchOffDelay> milliseconds.
|
||||
*/
|
||||
void addToJsonState(JsonObject& root)
|
||||
void addToJsonState(JsonObject &root)
|
||||
{
|
||||
root["PIRenabled"] = m_PIRenabled;
|
||||
root["PIRoffSec"] = (m_switchOffDelay / 1000);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||
* Values in the state object may be modified by connected clients
|
||||
* Read "PIRenabled" from json state and switch enable/disable the PIR sensor.
|
||||
* Read "PIRoffSec" from json state and adjust <m_switchOffDelay> milliseconds .
|
||||
* Read "PIRoffSec" from json state and adjust <m_switchOffDelay> milliseconds.
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root)
|
||||
void readFromJsonState(JsonObject &root)
|
||||
{
|
||||
if (root["PIRoffSec"] != nullptr)
|
||||
{
|
||||
if (root["PIRoffSec"] != nullptr) {
|
||||
m_switchOffDelay = (1000 * max(60UL, min(43200UL, root["PIRoffSec"].as<unsigned long>())));
|
||||
m_updateConfig = true;
|
||||
}
|
||||
|
||||
if (root["PIRenabled"] != nullptr) {
|
||||
if (root["PIRenabled"] && !m_PIRenabled) {
|
||||
if (root["PIRenabled"] != nullptr)
|
||||
{
|
||||
if (root["PIRenabled"] && !m_PIRenabled)
|
||||
{
|
||||
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
|
||||
newPIRsensorState(true, true);
|
||||
}
|
||||
else if(m_PIRenabled) {
|
||||
else if (m_PIRenabled)
|
||||
{
|
||||
detachInterrupt(PIRsensorPin);
|
||||
}
|
||||
m_PIRenabled = root["PIRenabled"];
|
||||
m_updateConfig = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* provide the changeable values
|
||||
*/
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("PIRsensorSwitch");
|
||||
top["PIRenabled"] = m_PIRenabled;
|
||||
top["PIRoffSec"] = m_switchOffDelay;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* restore the changeable values
|
||||
*/
|
||||
void readFromConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root["PIRsensorSwitch"];
|
||||
m_PIRenabled = (top["PIRenabled"] != nullptr ? top["PIRenabled"] : true);
|
||||
m_switchOffDelay = top["PIRoffSec"] | m_switchOffDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
@ -245,7 +335,32 @@ class PIRsensorSwitch : public Usermod {
|
||||
{
|
||||
return USERMOD_ID_PIRSWITCH;
|
||||
}
|
||||
|
||||
//More methods can be added in the future, this example will then be extended.
|
||||
//Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// PIRsensorSwitch static method implementations
|
||||
|
||||
volatile bool PIRsensorSwitch::newPIRsensorState(bool changeState, bool newState)
|
||||
{
|
||||
static volatile bool s_PIRsensorState = false;
|
||||
if (changeState)
|
||||
{
|
||||
s_PIRsensorState = newState;
|
||||
}
|
||||
return s_PIRsensorState;
|
||||
}
|
||||
|
||||
void IRAM_ATTR PIRsensorSwitch::ISR_PIRstateChange()
|
||||
{
|
||||
newPIRsensorState(true, true);
|
||||
}
|
||||
|
||||
PIRsensorSwitch *PIRsensorSwitch::PIRsensorSwitchInstance(PIRsensorSwitch *pInstance, bool bRemoveInstance)
|
||||
{
|
||||
static PIRsensorSwitch *s_pPIRsensorSwitch = nullptr;
|
||||
if (pInstance != nullptr || bRemoveInstance)
|
||||
{
|
||||
s_pPIRsensorSwitch = pInstance;
|
||||
}
|
||||
return s_pPIRsensorSwitch;
|
||||
}
|
||||
|
81
usermods/buzzer/usermod_v2_buzzer.h
Normal file
81
usermods/buzzer/usermod_v2_buzzer.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
#define USERMOD_ID_BUZZER 900
|
||||
#ifndef USERMOD_BUZZER_PIN
|
||||
#define USERMOD_BUZZER_PIN GPIO_NUM_32
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Usermods allow you to add own functionality to WLED more easily
|
||||
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
|
||||
*
|
||||
* Using a usermod:
|
||||
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
|
||||
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
|
||||
*/
|
||||
|
||||
class BuzzerUsermod : public Usermod {
|
||||
private:
|
||||
unsigned long lastTime_ = 0;
|
||||
unsigned long delay_ = 0;
|
||||
std::deque<std::pair<uint8_t, unsigned long>> sequence_ {};
|
||||
public:
|
||||
/*
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
void setup() {
|
||||
// Setup the pin, and default to LOW
|
||||
pinMode(USERMOD_BUZZER_PIN, OUTPUT);
|
||||
digitalWrite(USERMOD_BUZZER_PIN, LOW);
|
||||
|
||||
// Beep on startup
|
||||
sequence_.push_back({ HIGH, 50 });
|
||||
sequence_.push_back({ LOW, 0 });
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
void connected() {
|
||||
// Double beep on WiFi
|
||||
sequence_.push_back({ LOW, 100 });
|
||||
sequence_.push_back({ HIGH, 50 });
|
||||
sequence_.push_back({ LOW, 30 });
|
||||
sequence_.push_back({ HIGH, 50 });
|
||||
sequence_.push_back({ LOW, 0 });
|
||||
}
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
void loop() {
|
||||
if (sequence_.size() < 1) return; // Wait until there is a sequence
|
||||
if (millis() - lastTime_ <= delay_) return; // Wait until delay has elapsed
|
||||
|
||||
auto event = sequence_.front();
|
||||
sequence_.pop_front();
|
||||
|
||||
digitalWrite(USERMOD_BUZZER_PIN, event.first);
|
||||
delay_ = event.second;
|
||||
|
||||
lastTime_ = millis();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_BUZZER;
|
||||
}
|
||||
};
|
@ -149,7 +149,6 @@ public:
|
||||
|
||||
void Show()
|
||||
{
|
||||
byte b;
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
@ -191,6 +190,51 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool CanShow()
|
||||
{
|
||||
bool canShow = true;
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: canShow &= pGrb0->CanShow(); break;
|
||||
case 1: canShow &= pGrb1->CanShow(); break;
|
||||
case 2: canShow &= pGrb2->CanShow(); break;
|
||||
case 3: canShow &= pGrb3->CanShow(); break;
|
||||
case 4: canShow &= pGrb4->CanShow(); break;
|
||||
case 5: canShow &= pGrb5->CanShow(); break;
|
||||
case 6: canShow &= pGrb6->CanShow(); break;
|
||||
case 7: canShow &= pGrb7->CanShow(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NeoPixelType_Grbw:
|
||||
{
|
||||
for (uint8_t idx = 0; idx < numStrips; idx++)
|
||||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: canShow &= pGrbw0->CanShow(); break;
|
||||
case 1: canShow &= pGrbw1->CanShow(); break;
|
||||
case 2: canShow &= pGrbw2->CanShow(); break;
|
||||
case 3: canShow &= pGrbw3->CanShow(); break;
|
||||
case 4: canShow &= pGrbw4->CanShow(); break;
|
||||
case 5: canShow &= pGrbw5->CanShow(); break;
|
||||
case 6: canShow &= pGrbw6->CanShow(); break;
|
||||
case 7: canShow &= pGrbw7->CanShow(); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return canShow;
|
||||
}
|
||||
|
||||
void SetPixelColorRaw(uint16_t indexPixel, RgbwColor c)
|
||||
{
|
||||
// figure out which strip this pixel index is on
|
||||
|
153
wled00/FX.cpp
153
wled00/FX.cpp
@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "FX.h"
|
||||
#include "tv_colors.h"
|
||||
|
||||
#define IBN 5100
|
||||
#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3)
|
||||
@ -233,9 +234,9 @@ uint16_t WS2812FX::mode_random_color(void) {
|
||||
|
||||
/*
|
||||
* Lights every LED in a random color. Changes all LED at the same time
|
||||
// * to new random colors.
|
||||
* to new random colors.
|
||||
*/
|
||||
uint16_t WS2812FX::mode_dynamic(void) {
|
||||
uint16_t WS2812FX::dynamic(boolean smooth=false) {
|
||||
if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed
|
||||
|
||||
if(SEGENV.call == 0) {
|
||||
@ -252,12 +253,31 @@ uint16_t WS2812FX::mode_dynamic(void) {
|
||||
SEGENV.step = it;
|
||||
}
|
||||
|
||||
if (smooth) {
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
blendPixelColor(i, color_wheel(SEGENV.data[i]),16);
|
||||
}
|
||||
} else {
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
setPixelColor(i, color_wheel(SEGENV.data[i]));
|
||||
}
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
* Original effect "Dynamic"
|
||||
*/
|
||||
uint16_t WS2812FX::mode_dynamic(void) {
|
||||
return dynamic(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* effect "Dynamic" with smoth color-fading
|
||||
*/
|
||||
uint16_t WS2812FX::mode_dynamic_smooth(void) {
|
||||
return dynamic(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Does the "standby-breathing" of well known i-Devices.
|
||||
@ -990,6 +1010,12 @@ uint16_t WS2812FX::mode_merry_christmas(void) {
|
||||
return running(RED, GREEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Alternating red/white pixels running.
|
||||
*/
|
||||
uint16_t WS2812FX::mode_candy_cane(void) {
|
||||
return running(RED, WHITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Alternating orange/purple pixels running.
|
||||
@ -1754,19 +1780,22 @@ uint16_t WS2812FX::mode_fire_2012()
|
||||
|
||||
if (it != SEGENV.step)
|
||||
{
|
||||
uint8_t ignition = max(7,SEGLEN/10); // ignition area: 10% of segment length or minimum 7 pixels
|
||||
|
||||
// Step 1. Cool down every cell a little
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
SEGENV.data[i] = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
|
||||
uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
|
||||
heat[i] = (temp==0 && i<ignition) ? 2 : temp; // prevent ignition area from becoming black
|
||||
}
|
||||
|
||||
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
||||
for (uint16_t k= SEGLEN -1; k > 1; k--) {
|
||||
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
|
||||
heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2
|
||||
}
|
||||
|
||||
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
|
||||
if (random8() <= SEGMENT.intensity) {
|
||||
uint8_t y = random8(7);
|
||||
uint8_t y = random8(ignition);
|
||||
if (y < SEGLEN) heat[y] = qadd8(heat[y], random8(160,255));
|
||||
}
|
||||
SEGENV.step = it;
|
||||
@ -3727,3 +3756,117 @@ uint16_t WS2812FX::mode_washing_machine(void) {
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
Blends random colors across palette
|
||||
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
|
||||
*/
|
||||
uint16_t WS2812FX::mode_blends(void) {
|
||||
uint16_t dataSize = sizeof(uint32_t) * SEGLEN;
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
|
||||
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
|
||||
uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8;
|
||||
|
||||
for (int i = 0; i < SEGLEN; i++) {
|
||||
pixels[i] = color_blend(pixels[i], color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed);
|
||||
setPixelColor(i, pixels[i]);
|
||||
shift += 3;
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_FX_HIGH_FLASH_USE
|
||||
typedef struct TvSim {
|
||||
uint32_t totalTime = 0;
|
||||
uint32_t fadeTime = 0;
|
||||
uint32_t startTime = 0;
|
||||
uint32_t elapsed = 0;
|
||||
uint32_t pixelNum = 0;
|
||||
uint16_t pr = 0; // Prev R, G, B
|
||||
uint16_t pg = 0;
|
||||
uint16_t pb = 0;
|
||||
} tvSim;
|
||||
|
||||
#define numTVPixels (sizeof(tv_colors) / 2) // 2 bytes per Pixel (5/6/5)
|
||||
#endif
|
||||
|
||||
/*
|
||||
TV Simulator
|
||||
Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
|
||||
*/
|
||||
uint16_t WS2812FX::mode_tv_simulator(void) {
|
||||
#ifdef WLED_DISABLE_FX_HIGH_FLASH_USE
|
||||
return mode_static();
|
||||
#else
|
||||
uint16_t nr, ng, nb, r, g, b, i;
|
||||
uint8_t hi, lo, r8, g8, b8;
|
||||
|
||||
if (!SEGENV.allocateData(sizeof(tvSim))) return mode_static(); //allocation failed
|
||||
TvSim* tvSimulator = reinterpret_cast<TvSim*>(SEGENV.data);
|
||||
|
||||
// initialize start of the TV-Colors
|
||||
if (SEGENV.call == 0) {
|
||||
tvSimulator->pixelNum = ((uint8_t)random(18)) * numTVPixels / 18; // Begin at random movie (18 in total)
|
||||
}
|
||||
|
||||
// Read next 16-bit (5/6/5) color
|
||||
hi = pgm_read_byte(&tv_colors[tvSimulator->pixelNum * 2 ]);
|
||||
lo = pgm_read_byte(&tv_colors[tvSimulator->pixelNum * 2 + 1]);
|
||||
|
||||
// Expand to 24-bit (8/8/8)
|
||||
r8 = (hi & 0xF8) | (hi >> 5);
|
||||
g8 = ((hi << 5) & 0xff) | ((lo & 0xE0) >> 3) | ((hi & 0x06) >> 1);
|
||||
b8 = ((lo << 3) & 0xff) | ((lo & 0x1F) >> 2);
|
||||
|
||||
// Apply gamma correction, further expand to 16/16/16
|
||||
nr = (uint8_t)gamma8(r8) * 257; // New R/G/B
|
||||
ng = (uint8_t)gamma8(g8) * 257;
|
||||
nb = (uint8_t)gamma8(b8) * 257;
|
||||
|
||||
if (SEGENV.aux0 == 0) { // initialize next iteration
|
||||
SEGENV.aux0 = 1;
|
||||
|
||||
// increase color-index for next loop
|
||||
tvSimulator->pixelNum++;
|
||||
if (tvSimulator->pixelNum >= numTVPixels) tvSimulator->pixelNum = 0;
|
||||
|
||||
// randomize total duration and fade duration for the actual color
|
||||
tvSimulator->totalTime = random(250, 2500); // Semi-random pixel-to-pixel time
|
||||
tvSimulator->fadeTime = random(0, tvSimulator->totalTime); // Pixel-to-pixel transition time
|
||||
if (random(10) < 3) tvSimulator->fadeTime = 0; // Force scene cut 30% of time
|
||||
|
||||
tvSimulator->startTime = millis();
|
||||
} // end of initialization
|
||||
|
||||
// how much time is elapsed ?
|
||||
tvSimulator->elapsed = millis() - tvSimulator->startTime;
|
||||
|
||||
// fade from prev volor to next color
|
||||
if (tvSimulator->elapsed < tvSimulator->fadeTime) {
|
||||
r = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pr, nr);
|
||||
g = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pg, ng);
|
||||
b = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pb, nb);
|
||||
} else { // Avoid divide-by-zero in map()
|
||||
r = nr;
|
||||
g = ng;
|
||||
b = nb;
|
||||
}
|
||||
|
||||
// set strip color
|
||||
for (i = 0; i < SEGLEN; i++) {
|
||||
setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit
|
||||
}
|
||||
|
||||
// if total duration has passed, remember last color and restart the loop
|
||||
if ( tvSimulator->elapsed >= tvSimulator->totalTime) {
|
||||
tvSimulator->pr = nr; // Prev RGB = new RGB
|
||||
tvSimulator->pg = ng;
|
||||
tvSimulator->pb = nb;
|
||||
SEGENV.aux0 = 0;
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
#endif
|
||||
}
|
||||
|
51
wled00/FX.h
51
wled00/FX.h
@ -52,6 +52,9 @@
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
/* Disable effects with high flash memory usage (currently TV simulator) - saves 18.5kB */
|
||||
//#define WLED_DISABLE_FX_HIGH_FLASH_USE
|
||||
|
||||
/* Not used in all effects yet */
|
||||
#define WLED_FPS 42
|
||||
#define FRAMETIME (1000/WLED_FPS)
|
||||
@ -116,7 +119,8 @@
|
||||
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
|
||||
#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED )
|
||||
|
||||
#define MODE_COUNT 114
|
||||
|
||||
#define MODE_COUNT 118
|
||||
|
||||
#define FX_MODE_STATIC 0
|
||||
#define FX_MODE_BLINK 1
|
||||
@ -232,6 +236,10 @@
|
||||
#define FX_MODE_CHUNCHUN 111
|
||||
#define FX_MODE_DANCING_SHADOWS 112
|
||||
#define FX_MODE_WASHING_MACHINE 113
|
||||
#define FX_MODE_CANDY_CANE 114
|
||||
#define FX_MODE_BLENDS 115
|
||||
#define FX_MODE_TV_SIMULATOR 116
|
||||
#define FX_MODE_DYNAMIC_SMOOTH 117
|
||||
|
||||
class WS2812FX {
|
||||
typedef uint16_t (WS2812FX::*mode_ptr)(void);
|
||||
@ -316,9 +324,31 @@ class WS2812FX {
|
||||
WS2812FX::_usedSegmentData -= _dataLen;
|
||||
_dataLen = 0;
|
||||
}
|
||||
void reset(){next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; deallocateData();}
|
||||
|
||||
/**
|
||||
* If reset of this segment was request, clears runtime
|
||||
* settings of this segment.
|
||||
* Must not be called while an effect mode function is running
|
||||
* because it could access the data buffer and this method
|
||||
* may free that data buffer.
|
||||
*/
|
||||
void resetIfRequired() {
|
||||
if (_requiresReset) {
|
||||
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
|
||||
deallocateData();
|
||||
_requiresReset = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags that before the next effect is calculated,
|
||||
* the internal segment state should be reset.
|
||||
* Call resetIfRequired before calling the next effect function.
|
||||
*/
|
||||
void reset() { _requiresReset = true; }
|
||||
private:
|
||||
uint16_t _dataLen = 0;
|
||||
bool _requiresReset = false;
|
||||
} segment_runtime;
|
||||
|
||||
WS2812FX() {
|
||||
@ -437,6 +467,10 @@ class WS2812FX {
|
||||
_mode[FX_MODE_CHUNCHUN] = &WS2812FX::mode_chunchun;
|
||||
_mode[FX_MODE_DANCING_SHADOWS] = &WS2812FX::mode_dancing_shadows;
|
||||
_mode[FX_MODE_WASHING_MACHINE] = &WS2812FX::mode_washing_machine;
|
||||
_mode[FX_MODE_CANDY_CANE] = &WS2812FX::mode_candy_cane;
|
||||
_mode[FX_MODE_BLENDS] = &WS2812FX::mode_blends;
|
||||
_mode[FX_MODE_TV_SIMULATOR] = &WS2812FX::mode_tv_simulator;
|
||||
_mode[FX_MODE_DYNAMIC_SMOOTH] = &WS2812FX::mode_dynamic_smooth;
|
||||
|
||||
_brightness = DEFAULT_BRIGHTNESS;
|
||||
currentPalette = CRGBPalette16(CRGB::Black);
|
||||
@ -478,7 +512,9 @@ class WS2812FX {
|
||||
gammaCorrectCol = true,
|
||||
applyToAllSelected = true,
|
||||
segmentsAreIdentical(Segment* a, Segment* b),
|
||||
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p);
|
||||
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p),
|
||||
// return true if the strip is being sent pixel updates
|
||||
isUpdating(void);
|
||||
|
||||
uint8_t
|
||||
mainSegment = 0,
|
||||
@ -643,7 +679,11 @@ class WS2812FX {
|
||||
mode_flow(void),
|
||||
mode_chunchun(void),
|
||||
mode_dancing_shadows(void),
|
||||
mode_washing_machine(void);
|
||||
mode_washing_machine(void),
|
||||
mode_candy_cane(void),
|
||||
mode_blends(void),
|
||||
mode_tv_simulator(void),
|
||||
mode_dynamic_smooth(void);
|
||||
|
||||
private:
|
||||
NeoPixelWrapper *bus;
|
||||
@ -676,6 +716,7 @@ class WS2812FX {
|
||||
blink(uint32_t, uint32_t, bool strobe, bool),
|
||||
candle(bool),
|
||||
color_wipe(bool, bool),
|
||||
dynamic(bool),
|
||||
scan(bool),
|
||||
theater_chase(uint32_t, uint32_t, bool),
|
||||
running_base(bool),
|
||||
@ -731,7 +772,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([
|
||||
"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst",
|
||||
"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow",
|
||||
"Heartbeat","Pacifica","Candle Multi", "Solid Glitter","Sunrise","Phased","Twinkleup","Noise Pal", "Sine","Phased Noise",
|
||||
"Flow","Chunchun","Dancing Shadows","Washing Machine"
|
||||
"Flow","Chunchun","Dancing Shadows","Washing Machine","Candy Cane","Blends","TV Simulator","Dynamic Smooth"
|
||||
])=====";
|
||||
|
||||
|
||||
|
@ -44,6 +44,10 @@ const uint16_t customMappingTable[] = {
|
||||
const uint16_t customMappingSize = sizeof(customMappingTable)/sizeof(uint16_t); //30 in example
|
||||
#endif
|
||||
|
||||
#ifndef PWM_INDEX
|
||||
#define PWM_INDEX 0
|
||||
#endif
|
||||
|
||||
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst)
|
||||
{
|
||||
if (supportWhite == _useRgbw && countPixels == _length && _skipFirstMode == skipFirst) return;
|
||||
@ -76,6 +80,11 @@ void WS2812FX::service() {
|
||||
for(uint8_t i=0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
_segment_index = i;
|
||||
|
||||
// reset the segment runtime data if needed, called before isActive to ensure deleted
|
||||
// segment's buffers are cleared
|
||||
SEGENV.resetIfRequired();
|
||||
|
||||
if (SEGMENT.isActive())
|
||||
{
|
||||
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
|
||||
@ -218,7 +227,10 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
if (_callback) _callback();
|
||||
|
||||
// avoid race condition, caputre _callback value
|
||||
show_callback callback = _callback;
|
||||
if (callback) callback();
|
||||
|
||||
//power limit calculation
|
||||
//each LED can draw up 195075 "power units" (approx. 53mA)
|
||||
@ -291,10 +303,24 @@ void WS2812FX::show(void) {
|
||||
bus->SetBrightness(_brightness);
|
||||
}
|
||||
|
||||
// some buses send asynchronously and this method will return before
|
||||
// all of the data has been sent.
|
||||
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
|
||||
bus->Show();
|
||||
_lastShow = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a true value if any of the strips are still being updated.
|
||||
* On some hardware (ESP32), strip updates are done asynchronously.
|
||||
*/
|
||||
bool WS2812FX::isUpdating() {
|
||||
return !bus->CanShow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the next frame to be computed on all active segments.
|
||||
*/
|
||||
void WS2812FX::trigger() {
|
||||
_triggered = true;
|
||||
}
|
||||
@ -378,16 +404,16 @@ void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
}
|
||||
|
||||
void WS2812FX::setBrightness(uint8_t b) {
|
||||
if (gammaCorrectBri) b = gamma8(b);
|
||||
if (_brightness == b) return;
|
||||
_brightness = (gammaCorrectBri) ? gamma8(b) : b;
|
||||
_brightness = b;
|
||||
_segment_index = 0;
|
||||
if (b == 0) { //unfreeze all segments on power off
|
||||
if (_brightness == 0) { //unfreeze all segments on power off
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
_segments[i].setOption(SEG_OPTION_FREEZE, false);
|
||||
}
|
||||
#if LEDPIN == LED_BUILTIN
|
||||
if (!shouldStartBus)
|
||||
shouldStartBus = true;
|
||||
#endif
|
||||
} else {
|
||||
@ -890,13 +916,24 @@ void WS2812FX::handle_palette(void)
|
||||
*/
|
||||
uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
|
||||
{
|
||||
if (SEGMENT.palette == 0 && mcol < 3) return SEGCOLOR(mcol); //WS2812FX default
|
||||
if (SEGMENT.palette == 0 && mcol < 3) {
|
||||
uint32_t color = SEGCOLOR(mcol);
|
||||
if (pbri != 255) {
|
||||
CRGB crgb_color = col_to_crgb(color);
|
||||
crgb_color.nscale8_video(pbri);
|
||||
return crgb_to_col(crgb_color);
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t paletteIndex = i;
|
||||
if (mapping) paletteIndex = (i*255)/(SEGLEN -1);
|
||||
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGB fastled_col;
|
||||
fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
|
||||
return fastled_col.r*65536 + fastled_col.g*256 + fastled_col.b;
|
||||
|
||||
return crgb_to_col(fastled_col);
|
||||
}
|
||||
|
||||
//@returns `true` if color, mode, speed, intensity and palette match
|
||||
@ -924,7 +961,7 @@ void WS2812FX::setRgbwPwm(void) {
|
||||
_analogLastShow = nowUp;
|
||||
|
||||
RgbwColor c;
|
||||
uint32_t col = bus->GetPixelColorRgbw(0);
|
||||
uint32_t col = bus->GetPixelColorRgbw(PWM_INDEX);
|
||||
c.R = col >> 16; c.G = col >> 8; c.B = col; c.W = col >> 24;
|
||||
|
||||
byte b = getBrightness();
|
||||
|
@ -45,7 +45,7 @@
|
||||
//This can be useful if you want to chain multiple strings with incompatible color order
|
||||
//#define COLOR_ORDER_OVERRIDE
|
||||
#define COO_MIN 0
|
||||
#define COO_MAX 27 //not inclusive, this would set the override for LEDs 0-26
|
||||
#define COO_MAX 35 //not inclusive, this would set the override for LEDs 0-26
|
||||
#define COO_ORDER COL_ORDER_GRB
|
||||
|
||||
//END CONFIGURATION
|
||||
@ -296,7 +296,6 @@ public:
|
||||
|
||||
void Show()
|
||||
{
|
||||
byte b;
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb: _pGrb->Show(); break;
|
||||
@ -304,6 +303,22 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return true if enough time has passed since the last time Show() was called.
|
||||
* This also means that calling Show() will not cause any undue waiting. If the method for
|
||||
* the defined bus is hardware that sends asynchronously, then call CanShow() will let
|
||||
* you know if it has finished sending the data from the last Show().
|
||||
*/
|
||||
bool CanShow()
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case NeoPixelType_Grb: return _pGrb->CanShow();
|
||||
case NeoPixelType_Grbw: return _pGrbw->CanShow();
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPixelColor(uint16_t indexPixel, RgbwColor c)
|
||||
{
|
||||
RgbwColor col;
|
||||
|
@ -8,12 +8,12 @@
|
||||
uint16_t blHue = 0;
|
||||
byte blSat = 255;
|
||||
|
||||
void initBlynk(const char* auth)
|
||||
void initBlynk(const char *auth, const char *host, uint16_t port)
|
||||
{
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
if (!WLED_CONNECTED) return;
|
||||
blynkEnabled = (auth[0] != 0);
|
||||
if (blynkEnabled) Blynk.config(auth);
|
||||
if (blynkEnabled) Blynk.config(auth, host, port);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,7 @@ void deserializeConfig() {
|
||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||
CJSON(strip.reverseMode, hw_led[F("rev")]);
|
||||
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
|
||||
|
||||
JsonObject hw_led_ins_0 = hw_led[F("ins")][0];
|
||||
//bool hw_led_ins_0_en = hw_led_ins_0[F("en")]; // true
|
||||
@ -153,7 +154,6 @@ void deserializeConfig() {
|
||||
CJSON(bootPreset, def[F("ps")]);
|
||||
CJSON(turnOnAtBoot, def["on"]); // true
|
||||
CJSON(briS, def["bri"]); // 128
|
||||
if (briS == 0) briS = 255;
|
||||
|
||||
JsonObject def_cy = def[F("cy")];
|
||||
CJSON(presetCyclingEnabled, def_cy["on"]);
|
||||
@ -162,7 +162,7 @@ void deserializeConfig() {
|
||||
CJSON(presetCycleMax, def_cy[F("range")][1]);
|
||||
|
||||
tdd = def_cy[F("dur")] | -1;
|
||||
if (tdd >= 0) presetCycleTime = tdd * 100;
|
||||
if (tdd > 0) presetCycleTime = tdd;
|
||||
|
||||
JsonObject interfaces = doc["if"];
|
||||
|
||||
@ -212,6 +212,10 @@ void deserializeConfig() {
|
||||
if (tdd > 20 || tdd == 0)
|
||||
getStringFromJson(blynkApiKey, apikey, 36); //normally not present due to security
|
||||
|
||||
JsonObject if_blynk = interfaces[F("blynk")];
|
||||
getStringFromJson(blynkHost, if_blynk[F("host")], 33);
|
||||
CJSON(blynkPort, if_blynk[F("port")]);
|
||||
|
||||
JsonObject if_mqtt = interfaces[F("mqtt")];
|
||||
CJSON(mqttEnabled, if_mqtt[F("en")]);
|
||||
getStringFromJson(mqttServer, if_mqtt[F("broker")], 33);
|
||||
@ -251,7 +255,11 @@ void deserializeConfig() {
|
||||
CJSON(countdownMode, ol[F("cntdwn")]);
|
||||
overlayCurrent = overlayDefault;
|
||||
|
||||
JsonArray ol_cntdwn = ol[F("cntdwn")]; //[20,12,31,23,59,59]
|
||||
CJSON(overlayMin, ol[F("min")]);
|
||||
CJSON(overlayMax, ol[F("max")]);
|
||||
CJSON(analogClock12pixel, ol[F("o12pix")]);
|
||||
CJSON(analogClock5MinuteMarks, ol[F("o5m")]);
|
||||
CJSON(analogClockSecondsTrail, ol[F("osec")]);
|
||||
|
||||
//timed macro rules
|
||||
JsonObject tm = doc[F("timers")];
|
||||
@ -274,11 +282,13 @@ void deserializeConfig() {
|
||||
CJSON(timerMacro[it], timer[F("macro")]);
|
||||
|
||||
byte dowPrev = timerWeekday[it];
|
||||
bool actPrev = timerWeekday[it] & 0x01;
|
||||
//note: act is currently only 0 or 1.
|
||||
//the reason we are not using bool is that the on-disk type in 0.11.0 was already int
|
||||
int actPrev = timerWeekday[it] & 0x01;
|
||||
CJSON(timerWeekday[it], timer[F("dow")]);
|
||||
if (timerWeekday[it] != dowPrev) { //present in JSON
|
||||
timerWeekday[it] <<= 1; //add active bit
|
||||
bool act = timer[F("en")] | actPrev;
|
||||
int act = timer[F("en")] | actPrev;
|
||||
if (act) timerWeekday[it]++;
|
||||
}
|
||||
|
||||
@ -305,11 +315,11 @@ void deserializeConfig() {
|
||||
CJSON(DMXStart, dmx[F("start")]);
|
||||
CJSON(DMXStartLED,dmx[F("start-led")]);
|
||||
|
||||
JsonArray dmx_fixmap = dmx.createNestedArray("fixmap");
|
||||
JsonArray dmx_fixmap = dmx[F("fixmap")];
|
||||
it = 0;
|
||||
for (int i : dmx_fixmap) {
|
||||
if (it > 14) break;
|
||||
DMXFixtureMap[i] = i;
|
||||
CJSON(DMXFixtureMap[i],dmx_fixmap[i]);
|
||||
it++;
|
||||
}
|
||||
#endif
|
||||
@ -359,6 +369,7 @@ void serializeConfig() {
|
||||
ap[F("ssid")] = apSSID;
|
||||
ap[F("pskl")] = strlen(apPass);
|
||||
ap[F("chan")] = apChannel;
|
||||
ap[F("hide")] = apHide;
|
||||
ap[F("behav")] = apBehavior;
|
||||
|
||||
JsonArray ap_ip = ap.createNestedArray("ip");
|
||||
@ -378,6 +389,7 @@ void serializeConfig() {
|
||||
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||
hw_led[F("rev")] = strip.reverseMode;
|
||||
hw_led[F("rgbwm")] = strip.rgbwMode;
|
||||
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
|
||||
@ -478,7 +490,7 @@ void serializeConfig() {
|
||||
JsonArray def_cy_range = def_cy.createNestedArray("range");
|
||||
def_cy_range.add(presetCycleMin);
|
||||
def_cy_range.add(presetCycleMax);
|
||||
def_cy[F("dur")] = presetCycleTime / 100;
|
||||
def_cy[F("dur")] = presetCycleTime;
|
||||
}
|
||||
|
||||
JsonObject interfaces = doc.createNestedObject("if");
|
||||
@ -523,6 +535,8 @@ void serializeConfig() {
|
||||
if_va_macros.add(macroAlexaOff);
|
||||
JsonObject if_blynk = interfaces.createNestedObject("blynk");
|
||||
if_blynk[F("token")] = strlen(blynkApiKey) ? "Hidden":"";
|
||||
if_blynk[F("host")] = blynkHost;
|
||||
if_blynk[F("port")] = blynkPort;
|
||||
|
||||
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
|
||||
if_mqtt[F("en")] = mqttEnabled;
|
||||
@ -562,6 +576,12 @@ void serializeConfig() {
|
||||
ol[F("clock")] = overlayDefault;
|
||||
ol[F("cntdwn")] = countdownMode;
|
||||
|
||||
ol[F("min")] = overlayMin;
|
||||
ol[F("max")] = overlayMax;
|
||||
ol[F("o12pix")] = analogClock12pixel;
|
||||
ol[F("o5m")] = analogClock5MinuteMarks;
|
||||
ol[F("osec")] = analogClockSecondsTrail;
|
||||
|
||||
JsonObject timers = doc.createNestedObject("timers");
|
||||
|
||||
JsonObject cntdwn = timers.createNestedObject("cntdwn");
|
||||
|
937
wled00/data/index.css
Normal file
937
wled00/data/index.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1414
wled00/data/index.js
Normal file
1414
wled00/data/index.js
Normal file
File diff suppressed because it is too large
Load Diff
7
wled00/data/iro.js
Normal file
7
wled00/data/iro.js
Normal file
File diff suppressed because one or more lines are too long
8
wled00/data/rangetouch.js
Normal file
8
wled00/data/rangetouch.js
Normal file
@ -0,0 +1,8 @@
|
||||
// ==========================================================================
|
||||
// rangetouch.js v2.0.1
|
||||
// Making <input type="range"> work on touch devices
|
||||
// https://github.com/sampotts/rangetouch
|
||||
// License: The MIT License (MIT)
|
||||
// ==========================================================================
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define("RangeTouch",t):(e=e||self).RangeTouch=t()}(this,(function(){"use strict";function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function t(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function r(e){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?n(Object(i),!0).forEach((function(n){t(e,n,i[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):n(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}var i={addCSS:!0,thumbWidth:15,watch:!0};function u(e,t){return function(){return Array.from(document.querySelectorAll(t)).includes(this)}.call(e,t)}var o=function(e){return null!=e?e.constructor:null},c=function(e,t){return!!(e&&t&&e instanceof t)},l=function(e){return null==e},a=function(e){return o(e)===Object},s=function(e){return o(e)===String},f=function(e){return Array.isArray(e)},h=function(e){return c(e,NodeList)},d=s,y=f,b=h,m=function(e){return c(e,Element)},g=function(e){return c(e,Event)},p=function(e){return l(e)||(s(e)||f(e)||h(e))&&!e.length||a(e)&&!Object.keys(e).length};function v(e,t){if(1>t){var n=function(e){var t="".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return t?Math.max(0,(t[1]?t[1].length:0)-(t[2]?+t[2]:0)):0}(t);return parseFloat(e.toFixed(n))}return Math.round(e/t)*t}return function(){function t(e,n){(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,t),m(e)?this.element=e:d(e)&&(this.element=document.querySelector(e)),m(this.element)&&p(this.element.rangeTouch)&&(this.config=r({},i,{},n),this.init())}return n=t,c=[{key:"setup",value:function(e){var n=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},o=null;if(p(e)||d(e)?o=Array.from(document.querySelectorAll(d(e)?e:'input[type="range"]')):m(e)?o=[e]:b(e)?o=Array.from(e):y(e)&&(o=e.filter(m)),p(o))return null;var c=r({},i,{},n);if(d(e)&&c.watch){var l=new MutationObserver((function(n){Array.from(n).forEach((function(n){Array.from(n.addedNodes).forEach((function(n){m(n)&&u(n,e)&&new t(n,c)}))}))}));l.observe(document.body,{childList:!0,subtree:!0})}return o.map((function(e){return new t(e,n)}))}},{key:"enabled",get:function(){return"ontouchstart"in document.documentElement}}],(o=[{key:"init",value:function(){t.enabled&&(this.config.addCSS&&(this.element.style.userSelect="none",this.element.style.webKitUserSelect="none",this.element.style.touchAction="manipulation"),this.listeners(!0),this.element.rangeTouch=this)}},{key:"destroy",value:function(){t.enabled&&(this.config.addCSS&&(this.element.style.userSelect="",this.element.style.webKitUserSelect="",this.element.style.touchAction=""),this.listeners(!1),this.element.rangeTouch=null)}},{key:"listeners",value:function(e){var t=this,n=e?"addEventListener":"removeEventListener";["touchstart","touchmove","touchend"].forEach((function(e){t.element[n](e,(function(e){return t.set(e)}),!1)}))}},{key:"get",value:function(e){if(!t.enabled||!g(e))return null;var n,r=e.target,i=e.changedTouches[0],u=parseFloat(r.getAttribute("min"))||0,o=parseFloat(r.getAttribute("max"))||100,c=parseFloat(r.getAttribute("step"))||1,l=r.getBoundingClientRect(),a=100/l.width*(this.config.thumbWidth/2)/100;return 0>(n=100/l.width*(i.clientX-l.left))?n=0:100<n&&(n=100),50>n?n-=(100-2*n)*a:50<n&&(n+=2*(n-50)*a),u+v(n/100*(o-u),c)}},{key:"set",value:function(e){t.enabled&&g(e)&&!e.target.disabled&&(e.preventDefault(),e.target.value=this.get(e),function(e,t){if(e&&t){var n=new Event(t,{bubbles:!0});e.dispatchEvent(n)}}(e.target,"touchend"===e.type?"change":"input"))}}])&&e(n.prototype,o),c&&e(n,c),t;var n,o,c}()}));
|
||||
//# sourceMappingURL=rangetouch.js.map
|
@ -1,12 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8">
|
||||
<title>WLED Settings</title>
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
background: #222;
|
||||
height: 100;
|
||||
height: 100px;
|
||||
margin: 0;
|
||||
}
|
||||
html {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>DMX Settings</title>
|
||||
<html lang="en"><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>DMX Settings</title>
|
||||
<script>
|
||||
function GCH(num) {
|
||||
d=document;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=500">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta charset="utf-8">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>Sync Settings</title>
|
||||
<html lang="en"><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>Sync Settings</title>
|
||||
<script>var d=document;
|
||||
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");}
|
||||
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
|
||||
@ -34,7 +34,7 @@ UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required
|
||||
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br>
|
||||
Receive <input type="checkbox" name="RB">Brightness, <input type="checkbox" name="RC">Color, and <input type="checkbox" name="RX">Effects<br>
|
||||
Send notifications on direct change: <input type="checkbox" name="SD"><br>
|
||||
Send notifications on button press: <input type="checkbox" name="SB"><br>
|
||||
Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
|
||||
Send Alexa notifications: <input type="checkbox" name="SA"><br>
|
||||
Send Philips Hue change notifications: <input type="checkbox" name="SH"><br>
|
||||
Send Macro notifications: <input type="checkbox" name="SM"><br>
|
||||
@ -79,6 +79,8 @@ Alexa invocation name: <input name="AI" maxlength="32">
|
||||
This may impact the responsiveness of the ESP8266.</b><br>
|
||||
For best results, only use one of these services at a time.<br>
|
||||
(alternatively, connect a second ESP to them and use the UDP sync)<br><br>
|
||||
Host: <input name="BH" maxlength="32">
|
||||
Port: <input name="BP" type="number" min="1" max="65535" value="80" class="d5"><br>
|
||||
Device Auth token: <input name="BK" maxlength="33"><br>
|
||||
<i>Clear the token field to disable. </i><a href="https://github.com/Aircoookie/WLED/wiki/Blynk" target="_blank">Setup info</a>
|
||||
<h3>MQTT</h3>
|
||||
@ -88,7 +90,7 @@ Port: <input name="MQPORT" type="number" min="1" max="65535" class="d5"><br>
|
||||
<b>The MQTT credentials are sent over an unsecured connection.<br>
|
||||
Never use the MQTT password for another service!</b><br>
|
||||
Username: <input name="MQUSER" maxlength="40"><br>
|
||||
Password: <input type="password" input name="MQPASS" maxlength="40"><br>
|
||||
Password: <input type="password" name="MQPASS" maxlength="40"><br>
|
||||
Client ID: <input name="MQCID" maxlength="40"><br>
|
||||
Device Topic: <input name="MD" maxlength="32"><br>
|
||||
Group Topic: <input name="MG" maxlength="32"><br>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta charset="utf-8">
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<head lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=500">
|
||||
<title>UI Settings</title>
|
||||
@ -18,7 +18,8 @@
|
||||
"quick": "Quick color selectors",
|
||||
"hex": "HEX color input"
|
||||
},
|
||||
"pcmbot": "Show bottom tab bar in PC mode"
|
||||
"pcmbot": "Show bottom tab bar in PC mode",
|
||||
"pid": "Show preset IDs"
|
||||
},
|
||||
"theme":{
|
||||
"alpha": {
|
||||
@ -26,7 +27,8 @@
|
||||
"tab":"Button opacity"
|
||||
},
|
||||
"bg":{
|
||||
"url":"BG image URL"
|
||||
"url":"BG image URL",
|
||||
"random":"Random BG image"
|
||||
},
|
||||
"color":{
|
||||
"bg":"BG HEX color"
|
||||
@ -162,6 +164,24 @@
|
||||
var f = gId('theme_base');
|
||||
if (f) f.value = (gId('dm').checked) ? 'light':'dark';
|
||||
}
|
||||
|
||||
// random BG image
|
||||
function setRandomBg() {
|
||||
if (gId("theme_bg_random").checked) {
|
||||
gId("theme_bg_url").value = "https://picsum.photos/1920/1080";
|
||||
} else {
|
||||
gId("theme_bg_url").value = "";
|
||||
}
|
||||
|
||||
}
|
||||
function checkRandomBg() {
|
||||
if (gId("theme_bg_url").value === "https://picsum.photos/1920/1080") {
|
||||
gId("theme_bg_random").checked = true;
|
||||
} else {
|
||||
gId("theme_bg_random").checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
function GetV()
|
||||
{
|
||||
}
|
||||
@ -190,12 +210,14 @@
|
||||
<h3>UI Appearance</h3>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_labels" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_pcmbot" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_pid" class="agi cb"><br>
|
||||
I hate dark mode: <input type="checkbox" id="dm" onchange="UI()"><br>
|
||||
<span id="idonthateyou" style="display:none"><i>Why would you? </i>🥺<br></span>
|
||||
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_tab" class="agi"><br>
|
||||
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_bg" class="agi"><br>
|
||||
<span class="l"></span>: <input id="theme_color_bg" maxlength="9" class="agi"><br>
|
||||
<span class="l">BG image URL</span>: <input id="theme_bg_url" class="agi">
|
||||
<span class="l">BG image URL</span>: <input id="theme_bg_url" class="agi" oninput="checkRandomBg()"><br>
|
||||
<span class="l">Random BG image</span>: <input type="checkbox" id="theme_bg_random" class="agi cb" onchange="setRandomBg()"><br>
|
||||
<input id="theme_base" class="agi" style="display:none">
|
||||
<hr><button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button>
|
||||
</form>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=500">
|
||||
@ -51,7 +51,7 @@
|
||||
<h3>Configure Access Point</h3>
|
||||
AP SSID (leave empty for no AP):<br> <input name="AS" maxlength="32"><br>
|
||||
Hide AP name: <input type="checkbox" name="AH"><br>
|
||||
AP password (leave empty for open):<br> <input type="password" name="AP" maxlength="63"><br>
|
||||
AP password (leave empty for open):<br> <input type="password" name="AP" maxlength="63" pattern="(.{8,63})|()" title="Empty or min. 8 characters"><br>
|
||||
Access Point WiFi channel: <input name="AC" type="number" min="1" max="13" required><br>
|
||||
AP opens:
|
||||
<select name="AB">
|
||||
|
@ -4,7 +4,9 @@
|
||||
<head>
|
||||
<meta content='width=device-width' name='viewport'>
|
||||
<title>WLED Update</title>
|
||||
<script>function B() { window.history.back() }</script>
|
||||
<script>function B() { window.history.back() }
|
||||
function U() {document.getElementById("uf").style.display="none";document.getElementById("msg").style.display="block";}
|
||||
</script>
|
||||
<style>
|
||||
.bt {
|
||||
background: #333;
|
||||
@ -28,16 +30,23 @@
|
||||
color: #fff;
|
||||
line-height: 200%
|
||||
}
|
||||
|
||||
#msg {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>WLED Software Update</h2>Installed version: ##VERSION##<br>Download the latest binary: <a
|
||||
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
|
||||
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br>
|
||||
<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' class="bt" name='update'
|
||||
required><br><input type='submit' class="bt" value='Update!'></form><button type="button" class="bt"
|
||||
onclick="B()">Back</button>
|
||||
<h2>WLED Software Update</h2>
|
||||
<form method='POST' action='/update' id='uf' enctype='multipart/form-data' onsubmit="U()">
|
||||
Installed version: ##VERSION##<br>
|
||||
Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases" target="_blank">
|
||||
<img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br>
|
||||
<input type='file' class="bt" name='update' accept=".bin" required><br>
|
||||
<input type='submit' class="bt" value='Update!' ><br>
|
||||
<button type="button" class="bt" onclick="B()">Back</button></form>
|
||||
<div id="msg"><b>Updating...</b><br>Please do not close or refresh the page :)</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -15,7 +15,7 @@ void handleAlexa();
|
||||
void onAlexaChange(EspalexaDevice* dev);
|
||||
|
||||
//blynk.cpp
|
||||
void initBlynk(const char* auth);
|
||||
void initBlynk(const char* auth, const char* host, uint16_t port);
|
||||
void handleBlynk();
|
||||
void updateBlynk();
|
||||
|
||||
@ -240,6 +240,7 @@ void userLoop();
|
||||
void applyMacro(byte index);
|
||||
void deEEP();
|
||||
void deEEPSettings();
|
||||
void clearEEPROM();
|
||||
|
||||
//wled_serial.cpp
|
||||
void handleSerial();
|
||||
|
@ -36,17 +36,19 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
|
||||
|
||||
// Autogenerated from wled00/data/update.htm, do not edit!!
|
||||
const char PAGE_update[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta content="width=device-width" name="viewport">
|
||||
<title>WLED Update</title><script>function B(){window.history.back()}</script>
|
||||
<style>
|
||||
.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}
|
||||
</style></head><body><h2>WLED Software Update</h2>Installed version: 0.11.0<br>
|
||||
Download the latest binary: <a
|
||||
<title>WLED Update</title><script>
|
||||
function B(){window.history.back()}function U(){document.getElementById("uf").style.display="none",document.getElementById("msg").style.display="block"}
|
||||
</script><style>
|
||||
.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}#msg{display:none}
|
||||
</style></head><body><h2>WLED Software Update</h2><form method="POST"
|
||||
action="/update" id="uf" enctype="multipart/form-data" onsubmit="U()">
|
||||
Installed version: 0.11.1<br>Download the latest binary: <a
|
||||
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
|
||||
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square">
|
||||
</a><br><form method="POST" action="/update" enctype="multipart/form-data">
|
||||
<input type="file" class="bt" name="update" required><br><input type="submit"
|
||||
class="bt" value="Update!"></form><button type="button" class="bt"
|
||||
onclick="B()">Back</button></body></html>)=====";
|
||||
</a><br><input type="file" class="bt" name="update" accept=".bin" required><br>
|
||||
<input type="submit" class="bt" value="Update!"><br><button type="button"
|
||||
class="bt" onclick="B()">Back</button></form><div id="msg"><b>Updating...</b>
|
||||
<br>Please do not close or refresh the page :)</div></body></html>)=====";
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/welcome.htm, do not edit!!
|
||||
|
@ -10,9 +10,9 @@ const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings.htm, do not edit!!
|
||||
const char PAGE_settings[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta http-equiv="Content-Type"
|
||||
content="text/html; charset=windows-1252"><title>WLED Settings</title><style>
|
||||
body{text-align:center;background:#222;height:100;margin:0}html{--h:11.55vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;border:.3ch solid #333;display:inline-block;font-size:8vmin;height:var(--h);width:95%%;margin-top:2.4vh}
|
||||
const char PAGE_settings[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>WLED Settings
|
||||
</title><style>
|
||||
body{text-align:center;background:#222;height:100px;margin:0}html{--h:11.55vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;border:.3ch solid #333;display:inline-block;font-size:8vmin;height:var(--h);width:95%%;margin-top:2.4vh}
|
||||
</style><script>
|
||||
function BB(){window.frameElement&&(document.getElementById("b").style.display="none",document.documentElement.style.setProperty("--h","13.86vh"))}
|
||||
</script></head><body onload="BB()"><form action="/"><button type="submit"
|
||||
@ -27,8 +27,8 @@ action="/settings/time"><button type="submit">Time & Macros</button></form><form
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings_wifi.htm, do not edit!!
|
||||
const char PAGE_settings_wifi[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport"
|
||||
content="width=500"><title>WiFi Settings</title><script>
|
||||
const char PAGE_settings_wifi[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta
|
||||
name="viewport" content="width=500"><title>WiFi Settings</title><script>
|
||||
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#wifi-settings")}function B(){window.open("/settings","_self")}function GetV() {var d=document;
|
||||
%CSS%%SCSS%</head><body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post"><div class="helpB"><button
|
||||
@ -53,8 +53,9 @@ maxlength="32"> .local<br>Client IP: <span class="sip">Not connected</span><br>
|
||||
<h3>Configure Access Point</h3>AP SSID (leave empty for no AP):<br><input
|
||||
name="AS" maxlength="32"><br>Hide AP name: <input type="checkbox" name="AH"><br>
|
||||
AP password (leave empty for open):<br><input type="password" name="AP"
|
||||
maxlength="63"><br>Access Point WiFi channel: <input name="AC" type="number"
|
||||
min="1" max="13" required><br>AP opens: <select name="AB"><option value="0">
|
||||
maxlength="63" pattern="(.{8,63})|()" title="Empty or min. 8 characters"><br>
|
||||
Access Point WiFi channel: <input name="AC" type="number" min="1" max="13"
|
||||
required><br>AP opens: <select name="AB"><option value="0">
|
||||
No connection after boot</option><option value="1">Disconnected</option><option
|
||||
value="2">Always</option><option value="3">Never (not recommended)</option>
|
||||
</select><br>AP IP: <span class="sip">Not active</span><br><h3>Experimental</h3>
|
||||
@ -66,8 +67,8 @@ Save & Connect</button></form></body></html>)=====";
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings_leds.htm, do not edit!!
|
||||
const char PAGE_settings_leds[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport"
|
||||
content="width=500"><title>LED Settings</title><script>
|
||||
const char PAGE_settings_leds[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta
|
||||
name="viewport" content="width=500"><title>LED Settings</title><script>
|
||||
var d=document,laprev=55;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings")}function B(){window.open("/settings","_self")}function S(){GetV(),setABL()}function enABL(){var e=d.getElementById("able").checked;d.Sf.LA.value=e?laprev:0,d.getElementById("abl").style.display=e?"inline":"none",d.getElementById("psu2").style.display=e?"inline":"none",d.Sf.LA.value>0&&setABL()}function enLA(){var e=d.Sf.LAsel.value;d.Sf.LA.value=e,d.getElementById("LAdis").style.display=50==e?"inline":"none",UI()}function setABL(){switch(d.getElementById("able").checked=!0,d.Sf.LAsel.value=50,parseInt(d.Sf.LA.value)){case 0:d.getElementById("able").checked=!1,enABL();break;case 30:d.Sf.LAsel.value=30;break;case 35:d.Sf.LAsel.value=35;break;case 55:d.Sf.LAsel.value=55;break;case 255:d.Sf.LAsel.value=255;break;default:d.getElementById("LAdis").style.display="inline"}UI()}function UI(){var e=d.querySelectorAll(".wc"),l=e.length;for(i=0;i<l;i++)e[i].style.display=d.getElementById("rgbw").checked?"inline":"none";d.getElementById("ledwarning").style.display=d.Sf.LC.value>1e3?"inline":"none",d.getElementById("ampwarning").style.display=d.Sf.MA.value>7200?"inline":"none",255==d.Sf.LA.value?laprev=12:d.Sf.LA.value>0&&(laprev=d.Sf.LA.value);var n=Math.ceil((100+d.Sf.LC.value*laprev)/500)/2;n=n>5?Math.ceil(n):n;var t="",a=30==d.Sf.LAsel.value,s=255==d.Sf.LAsel.value;n<1.02&&!a&&!s?t="ESP 5V pin with 1A USB supply":(t+=a?"12V ":s?"WS2815 12V ":"5V ",t+=n,t+="A supply connected to LEDs");var u=Math.ceil((100+d.Sf.LC.value*laprev)/1500)/2,c="(for most effects, ~";c+=u=u>5?Math.ceil(u):u,c+="A is enough)<br>",d.getElementById("psu").innerHTML=t,d.getElementById("psu2").innerHTML=s?"":c}function GetV() {var d=document;
|
||||
%CSS%%SCSS%</head><body onload="S()"><form
|
||||
id="form_s" name="Sf" method="post"><div class="helpB"><button type="button"
|
||||
@ -135,8 +136,8 @@ onclick="B()">Back</button><button type="submit">Save</button></form></body>
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
|
||||
// Autogenerated from wled00/data/settings_dmx.htm, do not edit!!
|
||||
const char PAGE_settings_dmx[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta name="viewport" content="width=500"><meta
|
||||
charset="utf-8"><title>DMX Settings</title><script>
|
||||
const char PAGE_settings_dmx[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
|
||||
<meta charset="utf-8"><title>DMX Settings</title><script>
|
||||
function GCH(n){for(d=document,d.getElementById("dmxchannels").innerHTML+="",i=0;i<n;i++)d.getElementById("dmxchannels").innerHTML+="<span id=CH"+(i+1)+"s >Channel "+(i+1)+": <select name=CH"+(i+1)+' id="CH'+(i+1)+'"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n'}function mMap(){for(d=document,numCh=document.Sf.CN.value,numGap=document.Sf.CG.value,parseInt(numCh)>parseInt(numGap)?d.getElementById("gapwarning").style.display="block":d.getElementById("gapwarning").style.display="none",i=0;i<15;i++)i>=numCh?(d.getElementById("CH"+(i+1)+"s").style.opacity="0.5",d.getElementById("CH"+(i+1)).disabled=!0):(d.getElementById("CH"+(i+1)+"s").style.opacity="1",d.getElementById("CH"+(i+1)).disabled=!1)}function S(){GCH(15),GetV(),mMap()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX")}function B(){window.history.back()}function GetV() {var d=document;
|
||||
%CSS%%SCSS%</head><body onload="S()"><form
|
||||
id="form_s" name="Sf" method="post"><div class="helpB"><button type="button"
|
||||
@ -165,9 +166,9 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
|
||||
#endif
|
||||
|
||||
// Autogenerated from wled00/data/settings_ui.htm, do not edit!!
|
||||
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport"
|
||||
content="width=500"><title>UI Settings</title><script>
|
||||
var initial_ds,initial_st,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,i,t){for(var n=i,l=e.split("_"),s=l.length,o=0;o<s-1;o++){var a=l[o];n[a]||(n[a]={}),n=n[a]}n[l[s-1]]=t}function addRec(e,t="",n=null){var l="";for(i in e){var s=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(l+=`<h3>${n[i].LABEL}</h3>`),l+=addRec(e[i],s,n?n[i]:null);else{var o=s;if(n&&n[i]?o=n[i]:e[i+"LABEL"]&&(o=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var a=typeof e[i];gId(s)?("boolean"===a?gId(s).checked=e[i]:gId(s).value=e[i],gId(s).previousElementSibling.matches(".l")&&(gId(s).previousElementSibling.innerHTML=o)):"boolean"===a?l+=`${o}: <input class="agi cb" type="checkbox" id=${s} ${e[i]?"checked":""}><br>`:"number"===a?l+=`${o}: <input class="agi" type="number" id=${s} value=${e[i]}><br>`:"string"===a&&(l+=`${o}:<br><input class="agi" id=${s} value=${e[i]}><br>`)}}return l}function genForm(e){var i;i=addRec(e,"",l),gId("gen").innerHTML=i}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="⚠ Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),i=0;i<e.length;i++){var t=e[i],n=t.classList.contains("cb")?t.checked:t.value;set(t.id,sett,n),console.log(`${t.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(t){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="⚠ Settings JSON saving failed. ("+t+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function GetV() {var d=document;
|
||||
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta
|
||||
name="viewport" content="width=500"><title>UI Settings</title><script>
|
||||
var initial_ds,initial_st,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode",pid:"Show preset IDs"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL",random:"Random BG image"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,t,i){for(var n=t,l=e.split("_"),o=l.length,s=0;s<o-1;s++){var d=l[s];n[d]||(n[d]={}),n=n[d]}n[l[o-1]]=i}function addRec(e,t="",n=null){var l="";for(i in e){var o=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(l+=`<h3>${n[i].LABEL}</h3>`),l+=addRec(e[i],o,n?n[i]:null);else{var s=o;if(n&&n[i]?s=n[i]:e[i+"LABEL"]&&(s=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var d=typeof e[i];gId(o)?("boolean"===d?gId(o).checked=e[i]:gId(o).value=e[i],gId(o).previousElementSibling.matches(".l")&&(gId(o).previousElementSibling.innerHTML=s)):"boolean"===d?l+=`${s}: <input class="agi cb" type="checkbox" id=${o} ${e[i]?"checked":""}><br>`:"number"===d?l+=`${s}: <input class="agi" type="number" id=${o} value=${e[i]}><br>`:"string"===d&&(l+=`${s}:<br><input class="agi" id=${o} value=${e[i]}><br>`)}}return l}function genForm(e){var t;t=addRec(e,"",l),gId("gen").innerHTML=t}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="⚠ Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),t=0;t<e.length;t++){var i=e[t],n=i.classList.contains("cb")?i.checked:i.value;set(i.id,sett,n),console.log(`${i.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(i){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="⚠ Settings JSON saving failed. ("+i+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function setRandomBg(){gId("theme_bg_random").checked?gId("theme_bg_url").value="https://picsum.photos/1920/1080":gId("theme_bg_url").value=""}function checkRandomBg(){"https://picsum.photos/1920/1080"===gId("theme_bg_url").value?gId("theme_bg_random").checked=!0:gId("theme_bg_random").checked=!1}function GetV() {var d=document;
|
||||
%CSS%%SCSS%</head><body onload="S()"><form
|
||||
id="form_s" name="Sf" method="post"><div
|
||||
style="position:sticky;top:0;background-color:#222"><div class="helpB"><button
|
||||
@ -185,22 +186,25 @@ You will need to set them again if using a different browser, device or WLED IP
|
||||
<br>Refresh the main UI to apply changes.</i><br><div id="gen">
|
||||
Loading settings...</div><h3>UI Appearance</h3><span class="l"></span>: <input
|
||||
type="checkbox" id="comp_labels" class="agi cb"><br><span class="l"></span>:
|
||||
<input type="checkbox" id="comp_pcmbot" class="agi cb"><br>I hate dark mode:
|
||||
<input type="checkbox" id="dm" onchange="UI()"><br><span id="idonthateyou"
|
||||
style="display:none"><i>Why would you? </i>🥺<br></span><span class="l">
|
||||
</span>: <input type="number" min="0.0" max="1.0" step="0.01"
|
||||
id="theme_alpha_tab" class="agi"><br><span class="l"></span>: <input
|
||||
type="number" min="0.0" max="1.0" step="0.01" id="theme_alpha_bg" class="agi">
|
||||
<input type="checkbox" id="comp_pcmbot" class="agi cb"><br><span class="l">
|
||||
</span>: <input type="checkbox" id="comp_pid" class="agi cb"><br>
|
||||
I hate dark mode: <input type="checkbox" id="dm" onchange="UI()"><br><span
|
||||
id="idonthateyou" style="display:none"><i>Why would you? </i>🥺<br>
|
||||
</span><span class="l"></span>: <input type="number" min="0.0" max="1.0"
|
||||
step="0.01" id="theme_alpha_tab" class="agi"><br><span class="l"></span>: <input
|
||||
type="number" min="0.0" max="1.0" step="0.01" id="theme_alpha_bg" class="agi">
|
||||
<br><span class="l"></span>: <input id="theme_color_bg" maxlength="9"
|
||||
class="agi"><br><span class="l">BG image URL</span>: <input id="theme_bg_url"
|
||||
class="agi"> <input id="theme_base" class="agi" style="display:none"><hr><button
|
||||
type="button" onclick="B()">Back</button><button type="button"
|
||||
onclick="Save()">Save</button></form></body></html>)=====";
|
||||
class="agi" oninput="checkRandomBg()"><br><span class="l">Random BG image</span>
|
||||
: <input type="checkbox" id="theme_bg_random" class="agi cb"
|
||||
onchange="setRandomBg()"><br><input id="theme_base" class="agi"
|
||||
style="display:none"><hr><button type="button" onclick="B()">Back</button>
|
||||
<button type="button" onclick="Save()">Save</button></form></body></html>)=====";
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings_sync.htm, do not edit!!
|
||||
const char PAGE_settings_sync[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta name="viewport" content="width=500"><meta
|
||||
charset="utf-8"><title>Sync Settings</title><script>
|
||||
const char PAGE_settings_sync[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
|
||||
<meta charset="utf-8"><title>Sync Settings</title><script>
|
||||
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings")}function B(){window.open("/settings","_self")}function adj(){6454==d.Sf.DI.value?(1==d.Sf.DA.value&&(d.Sf.DA.value=0),1==d.Sf.EU.value&&(d.Sf.EU.value=0)):5568==d.Sf.DI.value&&(0==d.Sf.DA.value&&(d.Sf.DA.value=1),0==d.Sf.EU.value&&(d.Sf.EU.value=1))}function SP(){var e=d.Sf.DI.value;d.getElementById("xp").style.display=e>0?"none":"block",e>0&&(d.Sf.EP.value=e)}function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568:d.Sf.DI.value=5568;break;case 6454:d.Sf.DI.value=6454;break;case 4048:d.Sf.DI.value=4048}SP()}function S(){GetV(),SetVal()}function GetV() {
|
||||
%CSS%%SCSS%</head><body onload="S()"><form
|
||||
id="form_s" name="Sf" method="post"><div class="helpB"><button type="button"
|
||||
@ -219,7 +223,7 @@ type="number" min="1" max="65535" class="d5" required><br>Receive <input
|
||||
type="checkbox" name="RB">Brightness, <input type="checkbox" name="RC">
|
||||
Color, and <input type="checkbox" name="RX">Effects<br>
|
||||
Send notifications on direct change: <input type="checkbox" name="SD"><br>
|
||||
Send notifications on button press: <input type="checkbox" name="SB"><br>
|
||||
Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
|
||||
Send Alexa notifications: <input type="checkbox" name="SA"><br>
|
||||
Send Philips Hue change notifications: <input type="checkbox" name="SH"><br>
|
||||
Send Macro notifications: <input type="checkbox" name="SM"><br>
|
||||
@ -251,19 +255,20 @@ maxlength="32"><h3>Blynk</h3><b>
|
||||
Blynk, MQTT and Hue sync all connect to external hosts!<br>
|
||||
This may impact the responsiveness of the ESP8266.</b><br>
|
||||
For best results, only use one of these services at a time.<br>
|
||||
(alternatively, connect a second ESP to them and use the UDP sync)<br><br>
|
||||
Device Auth token: <input name="BK" maxlength="33"><br><i>
|
||||
Clear the token field to disable. </i><a
|
||||
(alternatively, connect a second ESP to them and use the UDP sync)<br><br>Host:
|
||||
<input name="BH" maxlength="32"> Port: <input name="BP" type="number" min="1"
|
||||
max="65535" value="80" class="d5"><br>Device Auth token: <input name="BK"
|
||||
maxlength="33"><br><i>Clear the token field to disable. </i><a
|
||||
href="https://github.com/Aircoookie/WLED/wiki/Blynk" target="_blank">Setup info
|
||||
</a><h3>MQTT</h3>Enable MQTT: <input type="checkbox" name="MQ"><br>Broker:
|
||||
<input name="MS" maxlength="32"> Port: <input name="MQPORT" type="number"
|
||||
min="1" max="65535" class="d5"><br><b>
|
||||
The MQTT credentials are sent over an unsecured connection.<br>
|
||||
Never use the MQTT password for another service!</b><br>Username: <input
|
||||
name="MQUSER" maxlength="40"><br>Password: <input type="password" input
|
||||
name="MQPASS" maxlength="40"><br>Client ID: <input name="MQCID" maxlength="40">
|
||||
<br>Device Topic: <input name="MD" maxlength="32"><br>Group Topic: <input
|
||||
name="MG" maxlength="32"><br><i>Reboot required to apply changes. </i><a
|
||||
name="MQUSER" maxlength="40"><br>Password: <input type="password" name="MQPASS"
|
||||
maxlength="40"><br>Client ID: <input name="MQCID" maxlength="40"><br>
|
||||
Device Topic: <input name="MD" maxlength="32"><br>Group Topic: <input name="MG"
|
||||
maxlength="32"><br><i>Reboot required to apply changes. </i><a
|
||||
href="https://github.com/Aircoookie/WLED/wiki/MQTT" target="_blank">MQTT info
|
||||
</a><h3>Philips Hue</h3><i>
|
||||
You can find the bridge IP and the light number in the 'About' section of the hue app.
|
||||
@ -282,8 +287,8 @@ type="submit">Save</button></form></body></html>)=====";
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings_time.htm, do not edit!!
|
||||
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta name="viewport" content="width=500"><meta
|
||||
charset="utf-8"><title>Time Settings</title><script>
|
||||
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
|
||||
<meta charset="utf-8"><title>Time Settings</title><script>
|
||||
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0],i=0;i<8;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
|
||||
%CSS%%SCSS%</head><body onload="S()"><form
|
||||
id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="helpB"><button
|
||||
@ -337,8 +342,8 @@ required><br><h3>Time-controlled presets</h3><div style="display:inline-block">
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings_sec.htm, do not edit!!
|
||||
const char PAGE_settings_sec[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta name="viewport" content="width=500"><meta
|
||||
charset="utf-8"><title>Misc Settings</title><script>
|
||||
const char PAGE_settings_sec[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
|
||||
<meta charset="utf-8"><title>Misc Settings</title><script>
|
||||
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings")}function B(){window.open("/settings","_self")}function U(){window.open("/update","_self")}function GetV() {var d=document;
|
||||
%CSS%%SCSS%</head><body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post"><div class="helpB"><button
|
||||
@ -358,7 +363,7 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
|
||||
<h3>Software Update</h3><button type="button" onclick="U()">Manual OTA Update
|
||||
</button><br>Enable ArduinoOTA: <input type="checkbox" name="AO"><br><h3>About
|
||||
</h3><a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a>
|
||||
version 0.11.0<br><br><a
|
||||
version 0.11.1<br><br><a
|
||||
href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About"
|
||||
target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
|
4176
wled00/html_ui.h
4176
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -95,9 +95,21 @@ void colorUpdated(int callMode)
|
||||
callMode != NOTIFIER_CALL_MODE_DIRECT_CHANGE &&
|
||||
callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
|
||||
|
||||
bool someSel = false;
|
||||
|
||||
if (callMode == NOTIFIER_CALL_MODE_NOTIFICATION) {
|
||||
someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
}
|
||||
|
||||
//Notifier: apply received FX to selected segments only if actually receiving FX
|
||||
if (someSel) strip.applyToAllSelected = receiveNotificationEffects;
|
||||
|
||||
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
|
||||
bool colChanged = colorChanged();
|
||||
|
||||
//Notifier: apply received color to selected segments only if actually receiving color
|
||||
if (someSel) strip.applyToAllSelected = receiveNotificationColor;
|
||||
|
||||
if (fxChanged || colChanged)
|
||||
{
|
||||
if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0;
|
||||
@ -107,7 +119,7 @@ void colorUpdated(int callMode)
|
||||
notify(callMode);
|
||||
|
||||
//set flag to update blynk and mqtt
|
||||
if (callMode != NOTIFIER_CALL_MODE_PRESET_CYCLE) interfaceUpdateCallMode = callMode;
|
||||
interfaceUpdateCallMode = callMode;
|
||||
} else {
|
||||
if (nightlightActive && !nightlightActiveOld &&
|
||||
callMode != NOTIFIER_CALL_MODE_NOTIFICATION &&
|
||||
@ -303,10 +315,10 @@ void handleNightlight()
|
||||
if (bri == 0 || nightlightActive) return;
|
||||
|
||||
if (presetCycCurr < presetCycleMin || presetCycCurr > presetCycleMax) presetCycCurr = presetCycleMin;
|
||||
applyPreset(presetCycCurr);
|
||||
applyPreset(presetCycCurr); //this handles colorUpdated() for us
|
||||
presetCycCurr++;
|
||||
if (presetCycCurr > 250) presetCycCurr = 1;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_PRESET_CYCLE);
|
||||
interfaceUpdateCallMode = 0; //disable updates to MQTT and Blynk
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,8 +163,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
alexaEnabled = request->hasArg(F("AL"));
|
||||
strlcpy(alexaInvocationName, request->arg(F("AI")).c_str(), 33);
|
||||
|
||||
strlcpy(blynkHost, request->arg("BH").c_str(), 33);
|
||||
t = request->arg(F("BP")).toInt();
|
||||
if (t > 0) blynkPort = t;
|
||||
|
||||
if (request->hasArg("BK") && !request->arg("BK").equals(F("Hidden"))) {
|
||||
strlcpy(blynkApiKey, request->arg("BK").c_str(), 36); initBlynk(blynkApiKey);
|
||||
strlcpy(blynkApiKey, request->arg("BK").c_str(), 36); initBlynk(blynkApiKey, blynkHost, blynkPort);
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
@ -266,6 +270,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
if (request->hasArg(F("RS"))) //complete factory reset
|
||||
{
|
||||
WLED_FS.format();
|
||||
clearEEPROM();
|
||||
serveMessage(request, 200, F("All Settings erased."), F("Connect to WLED-AP to setup again"),255);
|
||||
doReboot = true;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
*/
|
||||
/*
|
||||
* @title Espalexa library
|
||||
* @version 2.4.6
|
||||
* @version 2.5.0
|
||||
* @author Christian Schwinne
|
||||
* @license MIT
|
||||
* @contributors d-999
|
||||
@ -50,7 +50,7 @@
|
||||
#include "../network/Network.h"
|
||||
|
||||
#ifdef ESPALEXA_DEBUG
|
||||
#pragma message "Espalexa 2.4.6 debug mode"
|
||||
#pragma message "Espalexa 2.5.0 debug mode"
|
||||
#define EA_DEBUG(x) Serial.print (x)
|
||||
#define EA_DEBUGLN(x) Serial.println (x)
|
||||
#else
|
||||
@ -60,6 +60,7 @@
|
||||
|
||||
#include "EspalexaDevice.h"
|
||||
|
||||
#define DEVICE_UNIQUE_ID_LENGTH 12
|
||||
|
||||
class Espalexa {
|
||||
private:
|
||||
@ -116,17 +117,24 @@ private:
|
||||
return "";
|
||||
}
|
||||
|
||||
//Workaround functions courtesy of Sonoff-Tasmota
|
||||
uint32_t encodeLightId(uint8_t idx)
|
||||
void encodeLightId(uint8_t idx, char* out)
|
||||
{
|
||||
//Unique id must be 12 character len
|
||||
//use the last 10 characters of the MAC followed by the device id in hex value
|
||||
//uniqueId: aabbccddeeii
|
||||
|
||||
uint8_t mac[6];
|
||||
WiFi.macAddress(mac);
|
||||
uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4) | (idx & 0xF);
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t decodeLightId(uint32_t id) {
|
||||
return id & 0xF;
|
||||
//shift the mac address to the left (discard first byte)
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
mac[i] = mac[i+1];
|
||||
}
|
||||
mac[5] = idx;
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
sprintf(out + i*2, "%.2x", mac[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001)
|
||||
@ -136,10 +144,8 @@ private:
|
||||
if (deviceId >= currentDeviceCount) {strcpy(buf,"{}"); return;} //error
|
||||
EspalexaDevice* dev = devices[deviceId];
|
||||
|
||||
//char buf_bri[12] = "";
|
||||
//brightness support, add "bri" to JSON
|
||||
//if (dev->getType() != EspalexaDeviceType::onoff)
|
||||
// sprintf(buf_bri,",\"bri\":%u", dev->getLastValue()-1);
|
||||
char buf_lightid[13];
|
||||
encodeLightId(deviceId + 1, buf_lightid);
|
||||
|
||||
char buf_col[80] = "";
|
||||
//color support
|
||||
@ -161,10 +167,10 @@ private:
|
||||
|
||||
sprintf_P(buf, PSTR("{\"state\":{\"on\":%s,\"bri\":%u%s%s,\"alert\":\"none%s\",\"mode\":\"homeautomation\",\"reachable\":true},"
|
||||
"\"type\":\"%s\",\"name\":\"%s\",\"modelid\":\"%s\",\"manufacturername\":\"Philips\",\"productname\":\"E%u"
|
||||
"\",\"uniqueid\":\"%u\",\"swversion\":\"espalexa-2.4.6\"}")
|
||||
"\",\"uniqueid\":\"%s\",\"swversion\":\"espalexa-2.5.0\"}")
|
||||
|
||||
, (dev->getValue())?"true":"false", dev->getLastValue()-1, buf_col, buf_ct, buf_cm, typeString(dev->getType()),
|
||||
dev->getName().c_str(), modelidString(dev->getType()), static_cast<uint8_t>(dev->getType()), encodeLightId(deviceId+1));
|
||||
dev->getName().c_str(), modelidString(dev->getType()), static_cast<uint8_t>(dev->getType()), buf_lightid);
|
||||
}
|
||||
|
||||
//Espalexa status page /espalexa
|
||||
@ -186,7 +192,7 @@ private:
|
||||
}
|
||||
res += "\r\nFree Heap: " + (String)ESP.getFreeHeap();
|
||||
res += "\r\nUptime: " + (String)millis();
|
||||
res += "\r\n\r\nEspalexa library v2.4.6 by Christian Schwinne 2020";
|
||||
res += "\r\n\r\nEspalexa library v2.5.0 by Christian Schwinne 2020";
|
||||
server->send(200, "text/plain", res);
|
||||
}
|
||||
#endif
|
||||
@ -222,7 +228,7 @@ private:
|
||||
"<URLBase>http://%s:80/</URLBase>"
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
|
||||
"<friendlyName>Espalexa (%s)</friendlyName>"
|
||||
"<friendlyName>Espalexa (%s:80)</friendlyName>"
|
||||
"<manufacturer>Royal Philips Electronics</manufacturer>"
|
||||
"<manufacturerURL>http://www.philips.com</manufacturerURL>"
|
||||
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>"
|
||||
@ -237,8 +243,8 @@ private:
|
||||
|
||||
server->send(200, "text/xml", buf);
|
||||
|
||||
EA_DEBUG("Send setup.xml");
|
||||
//EA_DEBUGLN(setup_xml);
|
||||
EA_DEBUGLN("Send setup.xml");
|
||||
EA_DEBUGLN(buf);
|
||||
}
|
||||
|
||||
//init the server
|
||||
@ -298,7 +304,7 @@ private:
|
||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber
|
||||
"hue-bridgeid: %s\r\n"
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-%s::ssdp:all\r\n" // _uuid::_deviceType
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-%s::upnp:rootdevice\r\n" // _uuid::_deviceType
|
||||
"\r\n"),s,escapedMac.c_str(),escapedMac.c_str());
|
||||
|
||||
espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort());
|
||||
@ -359,26 +365,30 @@ public:
|
||||
|
||||
if (!udpConnected) return;
|
||||
int packetSize = espalexaUdp.parsePacket();
|
||||
if (!packetSize) return; //no new udp packet
|
||||
if (packetSize < 1) return; //no new udp packet
|
||||
|
||||
EA_DEBUGLN("Got UDP!");
|
||||
char packetBuffer[255]; //buffer to hold incoming udp packet
|
||||
uint16_t len = espalexaUdp.read(packetBuffer, 254);
|
||||
if (len > 0) {
|
||||
packetBuffer[len] = 0;
|
||||
}
|
||||
|
||||
unsigned char packetBuffer[packetSize+1]; //buffer to hold incoming udp packet
|
||||
espalexaUdp.read(packetBuffer, packetSize);
|
||||
packetBuffer[packetSize] = 0;
|
||||
|
||||
espalexaUdp.flush();
|
||||
if (!discoverable) return; //do not reply to M-SEARCH if not discoverable
|
||||
|
||||
String request = packetBuffer;
|
||||
if(request.indexOf("M-SEARCH") >= 0) {
|
||||
const char* request = (const char *) packetBuffer;
|
||||
if (strstr(request, "M-SEARCH") == nullptr) return;
|
||||
|
||||
EA_DEBUGLN(request);
|
||||
if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0 || request.indexOf("ssdp:all") > 0) {
|
||||
if (strstr(request, "ssdp:disc") != nullptr && //short for "ssdp:discover"
|
||||
(strstr(request, "upnp:rootd") != nullptr || //short for "upnp:rootdevice"
|
||||
strstr(request, "ssdp:all") != nullptr ||
|
||||
strstr(request, "asic:1") != nullptr )) //short for "device:basic:1"
|
||||
{
|
||||
EA_DEBUGLN("Responding search req...");
|
||||
respondToSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool addDevice(EspalexaDevice* d)
|
||||
{
|
||||
@ -452,13 +462,12 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (req.indexOf("state") > 0) //client wants to control light
|
||||
if ((req.indexOf("state") > 0) && (body.length() > 0)) //client wants to control light
|
||||
{
|
||||
server->send(200, "application/json", F("[{\"success\":{\"/lights/1/state/\": true}}]"));
|
||||
|
||||
uint32_t devId = req.substring(req.indexOf("lights")+7).toInt();
|
||||
EA_DEBUG("ls"); EA_DEBUGLN(devId);
|
||||
devId = decodeLightId(devId);
|
||||
EA_DEBUGLN(devId);
|
||||
devId--; //zero-based for devices array
|
||||
if (devId >= currentDeviceCount) return true; //return if invalid ID
|
||||
@ -530,7 +539,7 @@ public:
|
||||
String jsonTemp = "{";
|
||||
for (int i = 0; i<currentDeviceCount; i++)
|
||||
{
|
||||
jsonTemp += "\"" + String(encodeLightId(i+1)) + "\":";
|
||||
jsonTemp += "\"" + String(i+1) + "\":";
|
||||
char buf[512];
|
||||
deviceJsonString(i+1, buf);
|
||||
jsonTemp += buf;
|
||||
@ -540,7 +549,6 @@ public:
|
||||
server->send(200, "application/json", jsonTemp);
|
||||
} else //client wants one light (devId)
|
||||
{
|
||||
devId = decodeLightId(devId);
|
||||
EA_DEBUGLN(devId);
|
||||
if (devId > currentDeviceCount)
|
||||
{
|
||||
|
1506
wled00/tv_colors.h
Normal file
1506
wled00/tv_colors.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,8 +13,18 @@
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
#include "../usermods/Temperature/usermod_temperature.h"
|
||||
#endif
|
||||
|
||||
//#include "usermod_v2_empty.h"
|
||||
|
||||
#ifdef USERMOD_BUZZER
|
||||
#include "../usermods/buzzer/usermod_v2_buzzer.h"
|
||||
#endif
|
||||
|
||||
// BME280 v2 usermod. Define "USERMOD_BME280" in my_config.h
|
||||
#ifdef USERMOD_BME280
|
||||
#include "../usermods/BME280_v2/usermod_bme280.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
@ -23,8 +33,18 @@ void registerUsermods()
|
||||
* \/ \/ \/
|
||||
*/
|
||||
//usermods.add(new MyExampleUsermod());
|
||||
|
||||
#ifdef USERMOD_DALLASTEMPERATURE
|
||||
usermods.add(new UsermodTemperature());
|
||||
#endif
|
||||
|
||||
//usermods.add(new UsermodRenameMe());
|
||||
|
||||
#ifdef USERMOD_BUZZER
|
||||
usermods.add(new BuzzerUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_BME280
|
||||
usermods.add(new UsermodBME280());
|
||||
#endif
|
||||
}
|
@ -324,7 +324,8 @@ void WLED::beginStrip()
|
||||
|
||||
if (bootPreset > 0) applyPreset(bootPreset);
|
||||
if (turnOnAtBoot) {
|
||||
bri = (briS > 0) ? briS : 128;
|
||||
if (briS > 0) bri = briS;
|
||||
else if (bri == 0) bri = 128;
|
||||
} else {
|
||||
briLast = briS; bri = 0;
|
||||
}
|
||||
@ -377,6 +378,7 @@ void WLED::initAP(bool resetAP)
|
||||
if (udpPort2 > 0 && udpPort2 != ntpLocalPort && udpPort2 != udpPort && udpPort2 != udpRgbPort) {
|
||||
udp2Connected = notifier2Udp.begin(udpPort2);
|
||||
}
|
||||
e131.begin(false, e131Port, e131Universe, E131_MAX_UNIVERSE_COUNT);
|
||||
|
||||
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
|
||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
||||
@ -491,7 +493,7 @@ void WLED::initInterfaces()
|
||||
if (ntpEnabled)
|
||||
ntpConnected = ntpUdp.begin(ntpLocalPort);
|
||||
|
||||
initBlynk(blynkApiKey);
|
||||
initBlynk(blynkApiKey, blynkHost, blynkPort);
|
||||
e131.begin(e131Multicast, e131Port, e131Universe, E131_MAX_UNIVERSE_COUNT);
|
||||
reconnectHue();
|
||||
initMqtt();
|
||||
|
@ -3,12 +3,12 @@
|
||||
/*
|
||||
Main sketch, global variable declarations
|
||||
@title WLED project sketch
|
||||
@version 0.11.0
|
||||
@version 0.11.1
|
||||
@author Christian Schwinne
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2012020
|
||||
#define VERSION 2012210
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -22,10 +22,11 @@
|
||||
// You are required to disable over-the-air updates:
|
||||
//#define WLED_DISABLE_OTA // saves 14kb
|
||||
|
||||
// You need to choose some of these features to disable:
|
||||
// You can choose some of these features to disable:
|
||||
//#define WLED_DISABLE_ALEXA // saves 11kb
|
||||
//#define WLED_DISABLE_BLYNK // saves 6kb
|
||||
//#define WLED_DISABLE_CRONIXIE // saves 3kb
|
||||
//WLED_DISABLE_FX_HIGH_FLASH_USE (need to enable in PIO config or FX.h, saves 18kb)
|
||||
//#define WLED_DISABLE_HUESYNC // saves 4kb
|
||||
//#define WLED_DISABLE_INFRARED // there is no pin left for this on ESP8266-01, saves 12kb
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
@ -173,7 +174,7 @@
|
||||
#endif
|
||||
|
||||
// Global Variable definitions
|
||||
WLED_GLOBAL char versionString[] _INIT("0.11.0");
|
||||
WLED_GLOBAL char versionString[] _INIT("0.11.1");
|
||||
#define WLED_CODENAME "Mirai"
|
||||
|
||||
// AP and OTA default passwords (for maximum security change them!)
|
||||
@ -249,6 +250,8 @@ WLED_GLOBAL bool alexaEnabled _INIT(false); // enable devi
|
||||
WLED_GLOBAL char alexaInvocationName[33] _INIT("Light"); // speech control name of device. Choose something voice-to-text can understand
|
||||
|
||||
WLED_GLOBAL char blynkApiKey[36] _INIT(""); // Auth token for Blynk server. If empty, no connection will be made
|
||||
WLED_GLOBAL char blynkHost[33] _INIT("blynk-cloud.com"); // Default Blynk host
|
||||
WLED_GLOBAL uint16_t blynkPort _INIT(80); // Default Blynk port
|
||||
|
||||
WLED_GLOBAL uint16_t realtimeTimeoutMs _INIT(2500); // ms timeout of realtime mode before returning to normal mode
|
||||
WLED_GLOBAL int arlsOffset _INIT(0); // realtime LED offset
|
||||
|
@ -36,6 +36,20 @@
|
||||
//21-> 0.10.1p
|
||||
//22-> 2009260
|
||||
|
||||
/*
|
||||
* Erase all (pre 0.11) configuration data on factory reset
|
||||
*/
|
||||
void clearEEPROM()
|
||||
{
|
||||
EEPROM.begin(EEPSIZE);
|
||||
for (int i = 0; i < EEPSIZE; i++)
|
||||
{
|
||||
EEPROM.write(i, 0);
|
||||
}
|
||||
EEPROM.end();
|
||||
}
|
||||
|
||||
|
||||
void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
|
@ -323,13 +323,14 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappends('s',SET_F("AI"),alexaInvocationName);
|
||||
sappend('c',SET_F("SA"),notifyAlexa);
|
||||
sappends('s',SET_F("BK"),(char*)((blynkEnabled)?SET_F("Hidden"):""));
|
||||
sappends('s',SET_F("BH"),blynkHost);
|
||||
sappend('v',SET_F("BP"),blynkPort);
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
sappend('c',SET_F("MQ"),mqttEnabled);
|
||||
sappends('s',SET_F("MS"),mqttServer);
|
||||
sappend('v',SET_F("MQPORT"),mqttPort);
|
||||
sappends('s',SET_F("MQUSER"),mqttUser);
|
||||
sappends('s',SET_F("MQPASS"),mqttPass);
|
||||
byte l = strlen(mqttPass);
|
||||
char fpass[l+1]; //fill password field with ***
|
||||
fpass[l] = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user