We now accept and report state changes via JSON. Also added homeassistant MQTT autodiscovery.
This commit is contained in:
parent
a55e5e1ec2
commit
47cd48d572
@ -8,3 +8,4 @@ static PubSubClient mqtt(wifi);
|
|||||||
|
|
||||||
void mqtt_setup();
|
void mqtt_setup();
|
||||||
void mqtt_loop();
|
void mqtt_loop();
|
||||||
|
void mqtt_publish_current_state(String state);
|
26
include/state.h
Normal file
26
include/state.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include "prototypes.h"
|
||||||
|
|
||||||
|
class State {
|
||||||
|
AnimationMode _mode = AM_NONE;
|
||||||
|
uint16_t _duration = 0;
|
||||||
|
int16_t _brightness = -1;
|
||||||
|
uint8_t _speedup = 0;
|
||||||
|
CRGB _color = CRGB::Black;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void parse_json(char* str);
|
||||||
|
void set(String key, String value);
|
||||||
|
void commit();
|
||||||
|
static void publish_current_state();
|
||||||
|
|
||||||
|
void parse_state(String state);
|
||||||
|
void parse_mode(String mode);
|
||||||
|
void set_mode(AnimationMode mode);
|
||||||
|
void set_duration(uint16_t duration);
|
||||||
|
void set_brightness(uint8_t brightness);
|
||||||
|
void set_color(JsonObject rgb);
|
||||||
|
void set_speedup(uint8_t speedup);
|
||||||
|
};
|
@ -21,3 +21,4 @@ monitor_speed = 74880
|
|||||||
monitor_filters = default, time, send_on_enter, esp8266_exception_decoder
|
monitor_filters = default, time, send_on_enter, esp8266_exception_decoder
|
||||||
lib_deps = fastled/FastLED @ 3.4.0
|
lib_deps = fastled/FastLED @ 3.4.0
|
||||||
PubSubClient @ 2.8
|
PubSubClient @ 2.8
|
||||||
|
ArduinoJson
|
@ -9,6 +9,7 @@
|
|||||||
#include "corner.h"
|
#include "corner.h"
|
||||||
#include "prototypes.h"
|
#include "prototypes.h"
|
||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
std::vector<Node*> nodes;
|
std::vector<Node*> nodes;
|
||||||
std::list<Edge*> edges;
|
std::list<Edge*> edges;
|
||||||
@ -205,6 +206,7 @@ void loop() {
|
|||||||
FastLED.setBrightness(return_to_brightness);
|
FastLED.setBrightness(return_to_brightness);
|
||||||
return_to_brightness = -1;
|
return_to_brightness = -1;
|
||||||
}
|
}
|
||||||
|
State::publish_current_state();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
71
src/mqtt.cpp
71
src/mqtt.cpp
@ -1,16 +1,25 @@
|
|||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "prototypes.h"
|
#include "prototypes.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
void connect() {
|
void connect() {
|
||||||
LOGln("Connecting to MQTT broker...");
|
LOGln("Connecting to MQTT broker...");
|
||||||
if (mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_TOPIC_STATE, 0, true, "OFFLINE")) {
|
if (mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_TOPIC "state", 0, true, "OFFLINE")) {
|
||||||
LOGln("Connected.");
|
LOGln("Connected.");
|
||||||
mqtt.publish(MQTT_TOPIC_STATE, "ONLINE", true);
|
mqtt.publish(MQTT_TOPIC "available", "online", true);
|
||||||
char buffer[40];
|
char buffer[40];
|
||||||
snprintf(buffer, 40, "ONLINE %s", wifi.localIP().toString().c_str());
|
snprintf(buffer, 40, "online %s", wifi.localIP().toString().c_str());
|
||||||
mqtt.publish(MQTT_TOPIC_STATE_LONG, buffer, true);
|
mqtt.publish(MQTT_TOPIC "available_long", buffer, true);
|
||||||
mqtt.subscribe(MQTT_TOPIC_COMMANDS);
|
mqtt.subscribe(MQTT_TOPIC"cmnd");
|
||||||
|
String discovery_msg = "{\""
|
||||||
|
"\"~\":\"" MQTT_TOPIC ",\"avty_t\":\"~available\",\"cmd_t\":\"~cmnd\",\"stat_t\":\"~state\","
|
||||||
|
"\"name\":\"ESPleaf\",\"schema\":\"json\","
|
||||||
|
"\"brightness\":true,\"bri_scl\":255,"
|
||||||
|
"\"effect\":true,\"effect_list\":[\"off\",\"corners\",\"nodes\",\"flash\",\"static\"],"
|
||||||
|
"\"rgb\":true}";
|
||||||
|
|
||||||
|
mqtt.publish("homeassistant/light/" HOMEASSISTANT_OBJECT_ID "/config", discovery_msg.c_str(), true);
|
||||||
} else {
|
} else {
|
||||||
LOGln("Connection failed. Reason: %d", mqtt.state());
|
LOGln("Connection failed. Reason: %d", mqtt.state());
|
||||||
delay(1000);
|
delay(1000);
|
||||||
@ -19,10 +28,12 @@ LOGln("Connecting to MQTT broker...");
|
|||||||
|
|
||||||
void callback(char* topic, byte* pl, unsigned int length) {
|
void callback(char* topic, byte* pl, unsigned int length) {
|
||||||
pl[length] = 0;
|
pl[length] = 0;
|
||||||
|
State s;
|
||||||
|
if (pl[0]=='{') {
|
||||||
|
s.parse_json((char*)pl);
|
||||||
|
} else {
|
||||||
String payload((char*)pl);
|
String payload((char*)pl);
|
||||||
// Syntax: key=value&key2=value2...
|
// Syntax: key=value&key2=value2...
|
||||||
uint16_t duration = 0;
|
|
||||||
AnimationMode new_mode = AM_NONE;
|
|
||||||
String current_part;
|
String current_part;
|
||||||
LOGln("Received command %s", payload.c_str());
|
LOGln("Received command %s", payload.c_str());
|
||||||
while (payload.length() > 0) {
|
while (payload.length() > 0) {
|
||||||
@ -41,48 +52,10 @@ void callback(char* topic, byte* pl, unsigned int length) {
|
|||||||
}
|
}
|
||||||
String key = current_part.substring(0, offset);
|
String key = current_part.substring(0, offset);
|
||||||
String value = current_part.substring(offset+1);
|
String value = current_part.substring(offset+1);
|
||||||
LOGln(" Processing key %s with value %s", key.c_str(), value.c_str());
|
s.set(key, value);
|
||||||
|
|
||||||
if (key.equals("mode")) {
|
|
||||||
if (value.equals("nodes")) { new_mode = AM_NODES; }
|
|
||||||
else if (value.equals("first_node")) { new_mode = AM_FIRST_NODE; }
|
|
||||||
else if (value.equals("corners")) { new_mode = AM_CORNERS; }
|
|
||||||
else if (value.equals("first_corner")) { new_mode = AM_FIRST_CORNER; }
|
|
||||||
else if (value.equals("off")) { new_mode = AM_OFF; }
|
|
||||||
else if (value.equals("flash")) { new_mode = AM_FLASH; }
|
|
||||||
else if (value.equals("static")) { new_mode = AM_STATIC; }
|
|
||||||
else { LOGln("Unknown mode '%s'.", value.c_str()); }
|
|
||||||
} else if (key.equals("duration")) {
|
|
||||||
duration = value.toInt();
|
|
||||||
} else if (key.equals("brightness")) {
|
|
||||||
if (temp_mode_until == 0) {
|
|
||||||
return_to_brightness = FastLED.getBrightness();
|
|
||||||
}
|
|
||||||
FastLED.setBrightness(value.toInt());
|
|
||||||
} else if (key.equals("color")) {
|
|
||||||
if (value.equals("red")) { color = CRGB::Red; }
|
|
||||||
else if (value.equals("green")) { color = CRGB::Green; }
|
|
||||||
else if (value.equals("blue")) { color = CRGB::Blue; }
|
|
||||||
else if (value.equals("pink")) { color = CRGB::Pink; }
|
|
||||||
else if (value.equals("yellow")) { color = CRGB::Yellow; }
|
|
||||||
else if (value.equals("orange")) { color = CRGB::Orange; }
|
|
||||||
else { LOGln("Unknown color name %s.", value.c_str());}
|
|
||||||
} else if (key.equals("speedup")) {
|
|
||||||
speedup = value.toInt();
|
|
||||||
} else {
|
|
||||||
LOGln("Unknown key '%s'. (For reference: Value is '%s'.)", key.c_str(), value.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOGln("Finished processing the command.");
|
|
||||||
|
|
||||||
if (new_mode != AM_NONE) {
|
|
||||||
if (duration > 0) {
|
|
||||||
temp_mode = new_mode;
|
|
||||||
temp_mode_until = millis() + duration*1000;
|
|
||||||
} else {
|
|
||||||
mode = new_mode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mqtt_setup() {
|
void mqtt_setup() {
|
||||||
@ -98,3 +71,7 @@ void mqtt_loop() {
|
|||||||
}
|
}
|
||||||
mqtt.loop();
|
mqtt.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mqtt_publish_current_state(String state) {
|
||||||
|
mqtt.publish(MQTT_TOPIC "state", state.c_str(), true);
|
||||||
|
};
|
117
src/state.cpp
Normal file
117
src/state.cpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#include "state.h"
|
||||||
|
#include "tools.h"
|
||||||
|
#include "mqtt.h"
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
void State::parse_json(char* str) {
|
||||||
|
StaticJsonDocument<512> json;
|
||||||
|
deserializeJson(json, str);
|
||||||
|
|
||||||
|
if(json.containsKey("state")) parse_state(json["state"]);
|
||||||
|
if(json.containsKey("effect")) parse_mode(json["effect"]);
|
||||||
|
if(json.containsKey("duration")) set_duration(json["duration"]);
|
||||||
|
if(json.containsKey("brightness")) set_brightness(json["brightness"]);
|
||||||
|
if(json.containsKey("rgb")) set_color(json["rgb"]);
|
||||||
|
if(json.containsKey("speedup")) set_speedup(json["speedup"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::parse_state(String state) {
|
||||||
|
if (state.equals("ON")) {
|
||||||
|
set_mode(AM_CORNERS);
|
||||||
|
} else if (state.equals("OFF")) {
|
||||||
|
set_mode(AM_OFF);
|
||||||
|
} else {
|
||||||
|
LOGln("parse_state: Unknown state %s", state.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::parse_mode(String mode) {
|
||||||
|
if (mode.equals("nodes")) { set_mode(AM_NODES); }
|
||||||
|
else if (mode.equals("first_node")) { set_mode(AM_FIRST_NODE); }
|
||||||
|
else if (mode.equals("corners")) { set_mode(AM_CORNERS); }
|
||||||
|
else if (mode.equals("first_corner")) { set_mode(AM_FIRST_CORNER); }
|
||||||
|
else if (mode.equals("off")) { set_mode(AM_OFF); }
|
||||||
|
else if (mode.equals("flash")) { set_mode(AM_FLASH); }
|
||||||
|
else if (mode.equals("static")) { set_mode(AM_STATIC); }
|
||||||
|
else { LOGln("parse_mode: Unknown mode '%s'.", mode.c_str()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::set_mode(AnimationMode m) {
|
||||||
|
_mode = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::set_duration(uint16_t d) {
|
||||||
|
_duration = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::set_brightness(uint8_t b) {
|
||||||
|
_brightness = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::set_color(JsonObject rgb) {
|
||||||
|
if (!rgb.containsKey("r") || !rgb.containsKey("g") || !rgb.containsKey("b")) {
|
||||||
|
LOGln("set_color: Invalid rgb data.");
|
||||||
|
} else {
|
||||||
|
_color = CRGB(rgb["r"], rgb["g"], rgb["b"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::set_speedup(uint8_t s) {
|
||||||
|
_speedup = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::set(String key, String value) {
|
||||||
|
if (key.equals("state")) { parse_state(value); }
|
||||||
|
else if (key.equals("effect") || key.equals("mode")) { parse_mode(value); }
|
||||||
|
else if (key.equals("duration")) { set_duration(value.toInt()); }
|
||||||
|
else if (key.equals("brightness")) { set_brightness(value.toInt()); }
|
||||||
|
else if (key.equals("color")) {
|
||||||
|
if (value.equals("red")) { _color = CRGB::Red; }
|
||||||
|
else if (value.equals("green")) { _color = CRGB::Green; }
|
||||||
|
else if (value.equals("blue")) { _color = CRGB::Blue; }
|
||||||
|
else if (value.equals("pink")) { _color = CRGB::Pink; }
|
||||||
|
else if (value.equals("yellow")) { _color = CRGB::Yellow; }
|
||||||
|
else if (value.equals("orange")) { _color = CRGB::Orange; }
|
||||||
|
else { LOGln("Unknown color name %s.", value.c_str());}
|
||||||
|
} else if (key.equals("speedup")) { set_speedup(value.toInt()); }
|
||||||
|
else {
|
||||||
|
LOGln("Unknown key '%s'. (For reference: Value is '%s'.)", key.c_str(), value.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::commit() {
|
||||||
|
if (_brightness >= 0) {
|
||||||
|
if (temp_mode_until == 0) {
|
||||||
|
return_to_brightness = FastLED.getBrightness();
|
||||||
|
}
|
||||||
|
FastLED.setBrightness(_brightness);
|
||||||
|
}
|
||||||
|
if (_mode != AM_NONE) {
|
||||||
|
if (_duration > 0) {
|
||||||
|
temp_mode = _mode;
|
||||||
|
temp_mode_until = millis() + _duration*1000;
|
||||||
|
} else {
|
||||||
|
mode = _mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_color != CRGB(0, 0, 0)) {
|
||||||
|
color = _color;
|
||||||
|
}
|
||||||
|
|
||||||
|
publish_current_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::publish_current_state() {
|
||||||
|
StaticJsonDocument<512> json;
|
||||||
|
json["state"] = (mode==AM_OFF) ? "OFF" : "ON";
|
||||||
|
json["brightness"] = FastLED.getBrightness();
|
||||||
|
json["effect"] = mode;
|
||||||
|
JsonObject rgb = json.createNestedObject("rgb");
|
||||||
|
rgb["r"] = color.r;
|
||||||
|
rgb["g"] = color.g;
|
||||||
|
rgb["b"] = color.b;
|
||||||
|
String result = "";
|
||||||
|
serializeJson(json, result);
|
||||||
|
mqtt_publish_current_state(result);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user