Merge pull request #245 from HASwitchPlate/dev

HASPone 1.06
This commit is contained in:
Allen Derusha 2024-05-12 07:52:25 -04:00 committed by GitHub
commit 08130252d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 6244 additions and 4051 deletions

Binary file not shown.

5
Arduino_Sketch/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@ -6,7 +6,7 @@
// Home Automation Switch Plate
// https://github.com/aderusha/HASwitchPlate
//
// Copyright (c) 2021 Allen Derusha allen@derusha.org
// Copyright (c) 2024 Allen Derusha allen@derusha.org
//
// MIT License
//
@ -26,6 +26,7 @@
// OUT OF OR IN CONNECTION WITH THE PRODUCT OR THE USE OR OTHER DEALINGS IN THE PRODUCT.
////////////////////////////////////////////////////////////////////////////////////////////////////
#include <Arduino.h>
#include <FS.h>
#include <EEPROM.h>
#include <EspSaveCrash.h>
@ -43,6 +44,7 @@
#include <MQTT.h>
#include <SoftwareSerial.h>
#include <ESP8266Ping.h>
#include <LittleFS.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
// These defaults may be overwritten with values saved by the web interface
@ -62,8 +64,7 @@ char motionPinConfig[3] = "0";
char nextionBaud[7] = "115200";
////////////////////////////////////////////////////////////////////////////////////////////////////
const float haspVersion = 1.05; // Current HASPone software release version
const float haspVersion = 1.06; // Current HASPone software release version
const uint16_t mqttMaxPacketSize = 2048; // Size of buffer for incoming MQTT message
byte nextionReturnBuffer[128]; // Byte array to pass around data coming from the panel
uint8_t nextionReturnIndex = 0; // Index for nextionReturnBuffer
@ -75,7 +76,7 @@ bool shouldSaveConfig = false; // Flag to save json confi
bool nextionReportPage0 = false; // If false, don't report page 0 sendme
const unsigned long updateCheckInterval = 43200000; // Time in msec between update checks (12 hours)
unsigned long updateCheckTimer = updateCheckInterval; // Timer for update check
unsigned long updateCheckFirstRun = 60000; // First-run check offset
unsigned long updateCheckFirstRun = 30000; // First-run check offset
bool updateEspAvailable = false; // Flag for update check to report new ESP FW version
float updateEspAvailableVersion; // Float to hold the new ESP FW version number
bool updateLcdAvailable = false; // Flag for update check to report new LCD FW version
@ -87,6 +88,9 @@ bool nextionBufferOverrun = false; // Set to true if an overr
bool nextionAckEnable = false; // Wait for each Nextion command to be acked before continuing
bool nextionAckReceived = false; // Ack was received
bool rebootOnp0b1 = false; // When true, reboot device on button press of p[0].b[1]
bool rebootOnLongPress = true; // When true, reboot device on long press of any button
unsigned long rebootOnLongPressTimer = 0; // Clock for long press reboot timer
unsigned long rebootOnLongPressTimeout = 10000; // Timeout value for long press reboot timer
const unsigned long nextionAckTimeout = 1000; // Timeout to wait for an ack before throwing error
unsigned long nextionAckTimer = 0; // Timer to track Nextion ack
const unsigned long telnetInputMax = 128; // Size of user input buffer for user telnet session
@ -162,13 +166,67 @@ MDNSResponder::hMDNSService hMDNSService; // mDNS
EspSaveCrash SaveCrash; // Save crash details to flash
// URL for auto-update check of "version.json"
const char UPDATE_URL[] PROGMEM = "https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/update/version.json";
const char UPDATE_URL[] PROGMEM = "https://haswitchplate.com/update/version.json";
// Additional CSS style to match Hass theme
const char HASP_STYLE[] PROGMEM = "<style>button{background-color:#03A9F4;}body{width:60%;margin:auto;}input:invalid{border:1px solid red;}input[type=checkbox]{width:20px;}.wrap{text-align:left;display:inline-block;min-width:260px;max-width:1000px}</style>";
// Default link to compiled Arduino firmware image
String espFirmwareUrl = "https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/Arduino_Sketch/HASwitchPlate.ino.d1_mini.bin";
String espFirmwareUrl = "https://haswitchplate.com/update/HASwitchPlate.ino.d1_mini.bin";
// Default link to compiled Nextion firmware images
String lcdFirmwareUrl = "https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/Nextion_HMI/HASwitchPlate.tft";
String lcdFirmwareUrl = "https://haswitchplate.com/update/HASwitchPlate.tft";
void setup();
void loop();
void mqttConnect();
void mqttProcessInput(String &strTopic, String &strPayload);
void mqttStatusUpdate();
void mqttDiscovery();
void nextionHandleInput();
void nextionProcessInput();
void nextionSendCmd(const String &nextionCmd);
void nextionSetAttr(const String &hmiAttribute, const String &hmiValue);
void nextionGetAttr(const String &hmiAttribute);
void nextionParseJson(const String &strPayload);
void nextionOtaStartDownload(const String &lcdOtaUrl);
bool nextionOtaResponse();
bool nextionConnect();
void nextionSetSpeed();
void nextionReset();
void nextionUpdateProgress(const unsigned int &progress, const unsigned int &total);
void espWifiConnect();
void espWifiReconnect();
void espSetupOta();
void espStartOta(const String &espOtaUrl);
void espReset();
void configRead();
void configSaveCallback();
void configSave();
void configClearSaved();
void webHandleNotFound();
void webHandleRoot();
void webHandleSaveConfig();
void webHandleResetConfig();
void webHandleShowConfig();
void webHandleResetBacklight();
void webHandleFirmware();
void webHandleEspFirmware();
void webHandleLcdUpload();
void webHandleLcdUpdateSuccess();
void webHandleLcdUpdateFailure();
void webHandleLcdDownload();
void webHandleTftFileSize();
void webHandleReboot();
void espWifiConfigCallback(WiFiManager *myWiFiManager);
bool updateCheck();
void motionSetup();
void motionHandle();
void beepHandle();
void telnetHandleClient();
void debugPrintln(const String &debugText);
void debugPrint(const String &debugText);
void debugPrintCrash();
void debugPrintFile(const String &fileName);
String getSubtringField(String data, char separator, int index);
String printHex8(byte *data, uint8_t length);
////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
@ -652,65 +710,65 @@ void mqttProcessInput(String &strTopic, String &strPayload)
configSave();
}
else if (strTopic == (mqttCommandTopic + "/debugserialenabled") || strTopic == (mqttGroupCommandTopic + "/debugserialenabled"))
{ // '[...]/device/command/debugserialenabled' -m 'true' == enable serial debug output
{ // '[...]/device/command/debugserialenabled' -m 'true' == enable serial debug output
if (strPayload.equalsIgnoreCase("true"))
{
debugSerialEnabled = true;
configSave();
}
else if(strPayload.equalsIgnoreCase("false"))
else if (strPayload.equalsIgnoreCase("false"))
{
debugSerialEnabled = false;
configSave();
}
}
else if (strTopic == (mqttCommandTopic + "/debugtelnetenabled") || strTopic == (mqttGroupCommandTopic + "/debugtelnetenabled"))
{ // '[...]/device/command/debugtelnetenabled' -m 'true' == enable telnet debug output
{ // '[...]/device/command/debugtelnetenabled' -m 'true' == enable telnet debug output
if (strPayload.equalsIgnoreCase("true"))
{
debugTelnetEnabled = true;
configSave();
}
else if(strPayload.equalsIgnoreCase("false"))
else if (strPayload.equalsIgnoreCase("false"))
{
debugTelnetEnabled = false;
configSave();
}
}
else if (strTopic == (mqttCommandTopic + "/mdnsenabled") || strTopic == (mqttGroupCommandTopic + "/mdnsenabled"))
{ // '[...]/device/command/mdnsenabled' -m 'true' == enable mDNS responder
{ // '[...]/device/command/mdnsenabled' -m 'true' == enable mDNS responder
if (strPayload.equalsIgnoreCase("true"))
{
mdnsEnabled = true;
configSave();
}
else if(strPayload.equalsIgnoreCase("false"))
else if (strPayload.equalsIgnoreCase("false"))
{
mdnsEnabled = false;
configSave();
}
}
else if (strTopic == (mqttCommandTopic + "/beepenabled") || strTopic == (mqttGroupCommandTopic + "/beepenabled"))
{ // '[...]/device/command/beepenabled' -m 'true' == enable beep output on keypress
{ // '[...]/device/command/beepenabled' -m 'true' == enable beep output on keypress
if (strPayload.equalsIgnoreCase("true"))
{
beepEnabled = true;
configSave();
}
else if(strPayload.equalsIgnoreCase("false"))
else if (strPayload.equalsIgnoreCase("false"))
{
beepEnabled = false;
configSave();
}
}
else if (strTopic == (mqttCommandTopic + "/ignoretouchwhenoff") || strTopic == (mqttGroupCommandTopic + "/ignoretouchwhenoff"))
{ // '[...]/device/command/ignoretouchwhenoff' -m 'true' == disable actions on keypress
{ // '[...]/device/command/ignoretouchwhenoff' -m 'true' == disable actions on keypress
if (strPayload.equalsIgnoreCase("true"))
{
ignoreTouchWhenOff = true;
configSave();
}
else if(strPayload.equalsIgnoreCase("false"))
else if (strPayload.equalsIgnoreCase("false"))
{
ignoreTouchWhenOff = false;
configSave();
@ -844,8 +902,8 @@ void mqttProcessInput(String &strTopic, String &strPayload)
////////////////////////////////////////////////////////////////////////////////////////////////////
void mqttStatusUpdate()
{ // Periodically publish system status
String mqttSensorPayload = "{";
mqttSensorPayload += String(F("\"espVersion\":")) + String(haspVersion) + String(F(","));
String mqttSensorPayload = String(F("{\"espVersion\":"));
mqttSensorPayload += String(haspVersion) + String(F(","));
if (updateEspAvailable)
{
mqttSensorPayload += String(F("\"updateEspAvailable\":true,"));
@ -892,24 +950,23 @@ void mqttStatusUpdate()
////////////////////////////////////////////////////////////////////////////////////////////////////
void mqttDiscovery()
{ // Publish Home Assistant discovery messages
String macAddress = String(espMac[0], HEX) + String(F(":")) + String(espMac[1], HEX) + String(F(":")) + String(espMac[2], HEX) + String(F(":")) + String(espMac[3], HEX) + String(F(":")) + String(espMac[4], HEX) + String(F(":")) + String(espMac[5], HEX);
const String mqttDiscoveryDevice = String(F("\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"configuration_url\":\"http://")) + WiFi.localIP().toString() + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("},\"origin\":{\"name\":\"HASPone\",\"support_url\":\"https://haswitchplate.com\",\"sw\":")) + String(haspVersion) + String(F("}}"));
// light discovery for backlight
String mqttDiscoveryTopic = String(hassDiscovery) + String(F("/light/")) + String(haspNode) + String(F("/config"));
String mqttDiscoveryPayload = String(F("{\"name\":\"")) + String(haspNode) + String(F(" backlight\",\"command_topic\":\"")) + mqttLightCommandTopic + String(F("\",\"state_topic\":\"")) + mqttLightStateTopic + String(F("\",\"brightness_state_topic\":\"")) + mqttLightBrightStateTopic + String(F("\",\"brightness_command_topic\":\"")) + mqttLightBrightCommandTopic + String(F("\",\"availability_topic\":\"")) + mqttStatusTopic + String(F("\",\"brightness_scale\":100,\"unique_id\":\"")) + mqttClientId + String(F("-backlight\",\"payload_on\":\"ON\",\"payload_off\":\"OFF\",\"payload_available\":\"ON\",\"payload_not_available\":\"OFF\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
String mqttDiscoveryPayload = String(F("{\"name\":\"backlight\",\"object_id\":\"")) + String(haspNode) + String(F("_backlight\",\"command_topic\":\"")) + mqttLightCommandTopic + String(F("\",\"state_topic\":\"")) + mqttLightStateTopic + String(F("\",\"brightness_state_topic\":\"")) + mqttLightBrightStateTopic + String(F("\",\"brightness_command_topic\":\"")) + mqttLightBrightCommandTopic + String(F("\",\"availability_topic\":\"")) + mqttStatusTopic + String(F("\",\"brightness_scale\":100,\"unique_id\":\"")) + mqttClientId + String(F("-backlight\",\"payload_on\":\"ON\",\"payload_off\":\"OFF\",\"payload_available\":\"ON\",\"payload_not_available\":\"OFF\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
// sensor discovery for device telemetry
mqttDiscoveryTopic = String(hassDiscovery) + String(F("/sensor/")) + String(haspNode) + String(F("/config"));
mqttDiscoveryPayload = String(F("{\"name\":\"")) + String(haspNode) + String(F(" sensor\",\"json_attributes_topic\":\"")) + mqttSensorTopic + String(F("\",\"state_topic\":\"")) + mqttStatusTopic + String(F("\",\"unique_id\":\"")) + mqttClientId + String(F("-sensor\",\"icon\":\"mdi:cellphone-text\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
mqttDiscoveryPayload = String(F("{\"name\":\"sensor\",\"object_id\":\"")) + String(haspNode) + String(F("_sensor\",\"json_attributes_topic\":\"")) + mqttSensorTopic + String(F("\",\"state_topic\":\"")) + mqttStatusTopic + String(F("\",\"unique_id\":\"")) + mqttClientId + String(F("-sensor\",\"icon\":\"mdi:cellphone-text\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
// number discovery for active page
mqttDiscoveryTopic = String(hassDiscovery) + String(F("/number/")) + String(haspNode) + String(F("/config"));
mqttDiscoveryPayload = String(F("{\"name\":\"")) + String(haspNode) + String(F(" active page\",\"command_topic\":\"")) + mqttCommandTopic + String(F("/page\",\"state_topic\":\"")) + mqttStateTopic + String(F("/page\",\"step\":1,\"min\":0,\"max\":")) + String(nextionMaxPages) + String(F(",\"retain\":true,\"optimistic\":true,\"icon\":\"mdi:page-next-outline\",\"unique_id\":\"")) + mqttClientId + String(F("-page\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
mqttDiscoveryPayload = String(F("{\"name\":\"active page\",\"object_id\":\"")) + String(haspNode) + String(F("_active_page\",\"command_topic\":\"")) + mqttCommandTopic + String(F("/page\",\"state_topic\":\"")) + mqttStateTopic + String(F("/page\",\"step\":1,\"min\":0,\"max\":")) + String(nextionMaxPages) + String(F(",\"retain\":true,\"optimistic\":true,\"icon\":\"mdi:page-next-outline\",\"unique_id\":\"")) + mqttClientId + String(F("-page\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
@ -919,33 +976,32 @@ void mqttDiscovery()
// rgb light discovery for selectedforegroundcolor
mqttDiscoveryTopic = String(hassDiscovery) + String(F("/light/")) + String(haspNode) + String(F("/selectedforegroundcolor/config"));
mqttDiscoveryPayload = String(F("{\"name\":\"")) + String(haspNode) + String(F(" selected foreground color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedforegroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedforegroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-selectedforegroundcolor\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
mqttDiscoveryPayload = String(F("{\"name\":\"selected foreground color\",\"object_id\":\"")) + String(haspNode) + String(F("_selected_foreground_color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedforegroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedforegroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-selectedforegroundcolor\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
// rgb light discovery for selectedbackgroundcolor
mqttDiscoveryTopic = String(hassDiscovery) + String(F("/light/")) + String(haspNode) + String(F("/selectedbackgroundcolor/config"));
mqttDiscoveryPayload = String(F("{\"name\":\"")) + String(haspNode) + String(F(" selected background color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedbackgroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedbackgroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-selectedbackgroundcolor\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
mqttDiscoveryPayload = String(F("{\"name\":\"selected background color\",\"object_id\":\"")) + String(haspNode) + String(F("_selected_background_color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedbackgroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/selectedbackgroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-selectedbackgroundcolor\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
// rgb light discovery for unselectedforegroundcolor
mqttDiscoveryTopic = String(hassDiscovery) + String(F("/light/")) + String(haspNode) + String(F("/unselectedforegroundcolor/config"));
mqttDiscoveryPayload = String(F("{\"name\":\"")) + String(haspNode) + String(F(" unselected foreground color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedforegroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedforegroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-unselectedforegroundcolor\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
mqttDiscoveryPayload = String(F("{\"name\":\"unselected foreground color\",\"object_id\":\"")) + String(haspNode) + String(F("_unselected_foreground_color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedforegroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedforegroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-unselectedforegroundcolor\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
// rgb light discovery for unselectedbackgroundcolor
mqttDiscoveryTopic = String(hassDiscovery) + String(F("/light/")) + String(haspNode) + String(F("/unselectedbackgroundcolor/config"));
mqttDiscoveryPayload = String(F("{\"name\":\"")) + String(haspNode) + String(F(" unselected background color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedbackgroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedbackgroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-unselectedbackgroundcolor\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
mqttDiscoveryPayload = String(F("{\"name\":\"unselected background color\",\"object_id\":\"")) + String(haspNode) + String(F("_unselected_background_color\",\"command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedbackgroundcolor/switch\",\"state_topic\":\"hasp/")) + String(haspNode) + String(F("/alwayson\",\"rgb_command_topic\":\"hasp/")) + String(haspNode) + String(F("/light/unselectedbackgroundcolor/rgb\",\"rgb_command_template\":\"{{(red|bitwise_and(248)*256)+(green|bitwise_and(252)*8)+(blue|bitwise_and(248)/8)|int }}\",\"retain\":true,\"unique_id\":\"")) + mqttClientId + String(F("-unselectedbackgroundcolor\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
if (motionEnabled)
{ // binary_sensor for motion
String macAddress = String(espMac[0], HEX) + String(F(":")) + String(espMac[1], HEX) + String(F(":")) + String(espMac[2], HEX) + String(F(":")) + String(espMac[3], HEX) + String(F(":")) + String(espMac[4], HEX) + String(F(":")) + String(espMac[5], HEX);
String mqttDiscoveryTopic = String(hassDiscovery) + String(F("/binary_sensor/")) + String(haspNode) + String(F("-motion/config"));
String mqttDiscoveryPayload = String(F("{\"device_class\":\"motion\",\"name\":\"")) + String(haspNode) + String(F(" motion\",\"state_topic\":\"")) + mqttMotionStateTopic + String(F("\",\"unique_id\":\"")) + mqttClientId + String(F("-motion\",\"payload_on\":\"ON\",\"payload_off\":\"OFF\",\"device\":{\"identifiers\":[\"")) + mqttClientId + String(F("\"],\"name\":\"")) + String(haspNode) + String(F("\",\"manufacturer\":\"HASwitchPlate\",\"model\":\"HASPone v1.0.0\",\"sw_version\":")) + String(haspVersion) + String(F("}}"));
String mqttDiscoveryPayload = String(F("{\"device_class\":\"motion\",\"name\":\"motion\",\"object_id\":\"")) + String(haspNode) + String(F("_motion\",\"state_topic\":\"")) + mqttMotionStateTopic + String(F("\",\"unique_id\":\"")) + mqttClientId + String(F("-motion\",\"payload_on\":\"ON\",\"payload_off\":\"OFF\",")) + mqttDiscoveryDevice;
mqttClient.publish(mqttDiscoveryTopic, mqttDiscoveryPayload, true, 1);
debugPrintln(String(F("MQTT OUT: '")) + mqttDiscoveryTopic + String(F("' : '")) + String(mqttDiscoveryPayload) + String(F("'")));
}
@ -1250,6 +1306,10 @@ void nextionProcessInput()
debugPrintln(String(F("HMI IN: p[0].b[1] pressed during HASPone configuration, rebooting.")));
espReset();
}
if (rebootOnLongPress)
{
rebootOnLongPressTimer = millis();
}
}
else if (nextionButtonAction == 0x00)
{
@ -1280,6 +1340,12 @@ void nextionProcessInput()
nextionGetAttr("p[" + nextionPage + "].b[" + nextionButtonID + "].val");
}
}
if (rebootOnLongPress && (millis() - rebootOnLongPressTimer > rebootOnLongPressTimeout))
{
debugPrintln(String(F("HMI IN: Button long press, rebooting.")));
espReset();
}
rebootOnLongPressTimer = millis();
}
}
else if (nextionReturnBuffer[0] == 0x66)
@ -1624,8 +1690,6 @@ void nextionParseJson(const String &strPayload)
////////////////////////////////////////////////////////////////////////////////////////////////////
void nextionOtaStartDownload(const String &lcdOtaUrl)
{ // Upload firmware to the Nextion LCD via HTTP download
// based in large part on code posted by indev2 here:
// http://support.iteadstudio.com/support/discussions/topics/11000007686/page/2
uint32_t lcdOtaFileSize = 0;
String lcdOtaNextionCmd;
@ -1967,6 +2031,8 @@ void espWifiConnect()
nextionSendCmd("page 0");
}
WiFi.persistent(false);
enableWiFiAtBootTime();
WiFi.macAddress(espMac); // Read our MAC address and save it to espMac
WiFi.hostname(haspNode); // Assign our hostname before connecting to WiFi
WiFi.setAutoReconnect(true); // Tell WiFi to autoreconnect if connection has dropped
@ -1981,7 +2047,9 @@ void espWifiConnect()
{
nextionSetAttr("p[0].b[1].txt", "\"WiFi Connecting...\\r " + String(WiFi.SSID()) + "\"");
unsigned long connectTimer = millis() + 10000;
debugPrintln(String(F("WIFI: Connecting to previously-saved SSID: ")) + String(WiFi.SSID()));
WiFi.begin();
while ((WiFi.status() != WL_CONNECTED) && (millis() < connectTimer))
{
@ -2004,13 +2072,19 @@ void espWifiConnect()
{
yield();
}
if (WiFi.localIP().toString() == "(IP unset)")
{ // Check if we have our IP yet
debugPrintln(F("WIFI: Failed to lease address from DHCP, disconnecting and trying again"));
WiFi.disconnect();
}
}
}
if (WiFi.status() != WL_CONNECTED)
{ // We gave it a shot, still couldn't connect, so let WiFiManager run to make one last
// connection attempt and then flip to AP mode to collect credentials from the user.
WiFi.persistent(true);
WiFiManagerParameter custom_haspNodeHeader("<br/><b>HASPone Node</b>");
WiFiManagerParameter custom_haspNode("haspNode", "<br/>Node Name <small>(required: lowercase letters, numbers, and _ only)</small>", haspNode, 15, " maxlength=15 required pattern='[a-z0-9_]*'");
WiFiManagerParameter custom_groupName("groupName", "Group Name <small>(required)</small>", groupName, 15, " maxlength=15 required");
@ -2170,16 +2244,14 @@ void espSetupOta()
debugPrintln(F("ESP OTA: update start"));
nextionSetAttr("p[0].b[1].txt", "\"\\rHASPone update:\\r\\r\\r \"");
nextionSendCmd("page 0");
nextionSendCmd("vis 4,1");
});
nextionSendCmd("vis 4,1"); });
ArduinoOTA.onEnd([]()
{
debugPrintln(F("ESP OTA: update complete"));
nextionSetAttr("p[0].b[1].txt", "\"\\rHASPone update:\\r\\r Complete!\\rRestarting.\"");
nextionSendCmd("vis 4,1");
delay(1000);
espReset();
});
espReset(); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
{ nextionUpdateProgress(progress, total); });
ArduinoOTA.onError([](ota_error_t error)
@ -2198,8 +2270,7 @@ void espSetupOta()
nextionSendCmd("vis 4,0");
nextionSetAttr("p[0].b[1].txt", "\"HASPone update:\\r FAILED\\rerror: " + String(error) + "\"");
delay(1000);
nextionSendCmd("page " + String(nextionActivePage));
});
nextionSendCmd("page " + String(nextionActivePage)); });
ArduinoOTA.begin();
debugPrintln(F("ESP OTA: Over the Air firmware update ready"));
}
@ -2214,8 +2285,14 @@ void espStartOta(const String &espOtaUrl)
WiFiUDP::stopAll(); // Keep mDNS responder from breaking things
delay(1);
int startHost = espOtaUrl.indexOf("://") + 3; // Find the end of 'https://'
int endHost = espOtaUrl.indexOf('/', startHost); // Find the next '/' after 'https://'
String espOtaHost = espOtaUrl.substring(startHost, endHost); // Extract host
String espOtaUri = espOtaUrl.substring(endHost); // Extract URI
ESPhttpUpdate.rebootOnUpdate(false);
ESPhttpUpdate.onProgress(nextionUpdateProgress);
ESPhttpUpdate.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
t_httpUpdate_return espOtaUrlReturnCode;
if (espOtaUrl.startsWith(F("https")))
{
@ -2224,6 +2301,7 @@ void espStartOta(const String &espOtaUrl)
wifiEspOtaClientSecure.setInsecure();
wifiEspOtaClientSecure.setBufferSizes(512, 512);
espOtaUrlReturnCode = ESPhttpUpdate.update(wifiEspOtaClientSecure, espOtaUrl);
wifiEspOtaClientSecure.stop();
}
else
{
@ -2579,7 +2657,7 @@ void webHandleNotFound()
void webHandleRoot()
{ // http://plate01/
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -2832,7 +2910,7 @@ void webHandleRoot()
void webHandleSaveConfig()
{ // http://plate01/saveConfig
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3028,7 +3106,7 @@ void webHandleSaveConfig()
void webHandleResetConfig()
{ // http://plate01/resetConfig
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3070,7 +3148,7 @@ void webHandleResetConfig()
void webHandleResetBacklight()
{ // http://plate01/resetBacklight
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3101,7 +3179,7 @@ void webHandleResetBacklight()
void webHandleFirmware()
{ // http://plate01/firmware
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3145,7 +3223,7 @@ void webHandleFirmware()
{
webServer.sendContent(F("<font color='green'><b>HASPone LCD update available!</b></font>"));
}
webServer.sendContent(F("<br/><b>Update Nextion LCD from URL</b><small><i> http only</i></small>"));
webServer.sendContent(F("<br/><b>Update Nextion LCD from URL</b>"));
webServer.sendContent(F("<br/><input id='lcdFirmware' name='lcdFirmware' value='"));
webServer.sendContent(lcdFirmwareUrl);
webServer.sendContent(F("'><br/><br/><button type='submit'>Update LCD from URL</button></form>"));
@ -3174,7 +3252,7 @@ void webHandleFirmware()
void webHandleEspFirmware()
{ // http://plate01/espfirmware
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3209,7 +3287,7 @@ void webHandleLcdUpload()
// Upload firmware to the Nextion LCD via HTTP upload
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3443,7 +3521,7 @@ void webHandleLcdUpload()
void webHandleLcdUpdateSuccess()
{ // http://plate01/lcdOtaSuccess
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3471,7 +3549,7 @@ void webHandleLcdUpdateSuccess()
void webHandleLcdUpdateFailure()
{ // http://plate01/lcdOtaFailure
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3499,7 +3577,7 @@ void webHandleLcdUpdateFailure()
void webHandleLcdDownload()
{ // http://plate01/lcddownload
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3528,7 +3606,7 @@ void webHandleLcdDownload()
void webHandleTftFileSize()
{ // http://plate01/tftFileSize
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3548,7 +3626,7 @@ void webHandleTftFileSize()
void webHandleReboot()
{ // http://plate01/reboot
if (configPassword[0] != '\0')
{ //Request HTTP auth if configPassword is set
{ // Request HTTP auth if configPassword is set
if (!webServer.authenticate(configUser, configPassword))
{
return webServer.requestAuthentication();
@ -3726,12 +3804,12 @@ void telnetHandleClient()
{
telnetClient.stop(); // client disconnected
}
telnetClient = telnetServer.available(); // ready for new client
telnetInputIndex = 0; // reset input buffer index
telnetClient = telnetServer.accept(); // ready for new client
telnetInputIndex = 0; // reset input buffer index
}
else
{
telnetServer.available().stop(); // have client, block new connections
telnetServer.accept().stop(); // have client, block new connections
}
}
// Handle client input from telnet connection.

View File

@ -9,17 +9,17 @@
; https://docs.platformio.org/page/projectconf.html
[env:d1_mini]
platform = https://github.com/platformio/platform-espressif8266.git @ ^3.2.0
platform = https://github.com/platformio/platform-espressif8266.git @ ^4.2.1
board = d1_mini
framework = arduino
board_build.f_cpu = 160000000L
board_build.ldscript = eagle.flash.4m1m.ld
build_flags =
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191122
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
lib_deps =
bblanchon/ArduinoJson @ ^6.18.5
256dpi/MQTT @ ^2.5.0
dancol90/ESP8266Ping @ ^1.0
krzychb/EspSaveCrash @ ^1.2.0
https://github.com/tzapu/WiFiManager.git#8452df79bbc55265d6a999d7384204220f4d22c6
256dpi/MQTT @ ^2.5.2
dancol90/ESP8266Ping @ ^1.1.0
bblanchon/ArduinoJson @ ^7.0.4
krzychb/EspSaveCrash @ ^1.3.0
https://github.com/tzapu/WiFiManager.git#e978bc059c522404c01e06cd136fcf23234eb784

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] activates a page"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone activates a selected page after a specified period of inactivity"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -122,7 +122,7 @@ condition:
action:
- delay:
seconds: "{{idletime|int}}"
seconds: "{{idletime|int(default=30)}}"
- condition: template
value_template: >-

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] has theme colors applied"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description

View File

@ -0,0 +1,326 @@
blueprint:
name: "HASPone buttons have theme colors applied"
description: |
## Blueprint Version: `1.06.00`
## Description
Several buttons on the HASPone will have the current device theme or custom colors applied.
## HASPone Page and Button Reference
<details>
The images below show each available HASPone page along with the layout of available button objects.
| Page 0 | Pages 1-3 | Pages 4-5 |
|--------|-----------|-----------|
| ![Page 0](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p0_Init_Screen.png) | ![Pages 1-3](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p1-p3_4buttons.png) | ![Pages 4-5](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p4-p5_3sliders.png) |
| Page 6 | Page 7 | Page 8 |
|--------|--------|--------|
| ![Page 6](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p6_8buttons.png) | ![Page 7](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p7_12buttons.png) | ![Page 8](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p8_5buttons+1slider.png) |
| Page 9 | Page 10 | Page 11 |
|--------|---------|---------|
| ![Page 9](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p9_9buttons.png) | ![Page 10](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p10_5buttons.png) | ![Page 11](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p11_1button+1slider.png)
</details>
## Nextion color codes
<details>
The Nextion environment utilizes RGB 565 encoding. [Use this handy convertor](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to select your colors and convert to the RGB 565 format.
Here are some example colors:
| Color | Code |
|--------|-------|
| White | 65535 |
| Black | 0 |
| Grey | 25388 |
| Red | 63488 |
| Green | 2016 |
| Blue | 31 |
| Yellow | 65504 |
| Orange | 64512 |
| Brown | 48192 |
</details>
domain: automation
input:
haspdevice:
name: "HASPone Device"
description: "Select the HASPone device"
selector:
device:
integration: mqtt
manufacturer: "HASwitchPlate"
model: "HASPone v1.0.0"
objects:
name: "HASPone buttons"
description: "Apply the current theme or colors defined below to all of the objects in this list"
default:
- p[1].b[4]
- p[1].b[5]
- p[1].b[6]
- p[1].b[7]
selector:
object:
selected_fgcolor:
name: "Selected foreground color"
description: 'Selected foreground color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme selected foreground color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
selected_bgcolor:
name: "Selected background color"
description: 'Selected background color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme selected background color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
unselected_fgcolor:
name: "Unselected foreground color"
description: 'Unselected foreground color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme unselected foreground color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
unselected_bgcolor:
name: "Unselected background color"
description: 'Unselected background color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme unselected background color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
mode: parallel
max_exceeded: silent
variables:
haspdevice: !input haspdevice
haspname: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{- entity|regex_replace(find="^sensor\.", replace="", ignorecase=true)|regex_replace(find="_sensor(?:_\d+|)$", replace="", ignorecase=true) -}}
{%- endif -%}
{%- endfor -%}
objects: !input objects
selected_fgcolor: !input selected_fgcolor
selected_bgcolor: !input selected_bgcolor
unselected_fgcolor: !input unselected_fgcolor
unselected_bgcolor: !input unselected_bgcolor
# haspobject: '{{ "p[" ~ hasppage ~ "].b[" ~ haspbutton ~ "]" }}'
# commandtopic: '{{ "hasp/" ~ haspname ~ "/command/" ~ haspobject }}'
jsoncommandtopic: '{{ "hasp/" ~ haspname ~ "/command/json" }}'
selectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedforegroundcolor/rgb" }}'
selectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedbackgroundcolor/rgb" }}'
unselectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedforegroundcolor/rgb" }}'
unselectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedbackgroundcolor/rgb" }}'
selectedfg: >-
{%- if (selected_fgcolor|int) >= 0 -%}
{{ selected_fgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_selected_foreground_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
selectedbg: >-
{%- if (selected_bgcolor|int) >= 0 -%}
{{ selected_bgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_selected_background_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
unselectedfg: >-
{%- if (unselected_fgcolor|int) >= 0 -%}
{{ unselected_fgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_unselected_foreground_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
unselectedbg: >-
{%- if (unselected_bgcolor|int) >= 0 -%}
{{ unselected_bgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_unselected_background_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
trigger_variables:
haspdevice: !input haspdevice
haspname: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{- entity|regex_replace(find="^sensor\.", replace="", ignorecase=true)|regex_replace(find="_sensor(?:_\d+|)$", replace="", ignorecase=true) -}}
{%- endif -%}
{%- endfor -%}
haspsensor: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{ entity }}
{%- endif -%}
{%- endfor -%}
jsontopic: '{{ "hasp/" ~ haspname ~ "/state/json" }}'
selectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedforegroundcolor/rgb" }}'
selectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedbackgroundcolor/rgb" }}'
unselectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedforegroundcolor/rgb" }}'
unselectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedbackgroundcolor/rgb" }}'
trigger:
- platform: homeassistant
event: start
- platform: template
value_template: "{{ is_state(haspsensor, 'ON') }}"
- platform: mqtt
topic: "{{selectedfgtopic}}"
- platform: mqtt
topic: "{{selectedbgtopic}}"
- platform: mqtt
topic: "{{unselectedfgtopic}}"
- platform: mqtt
topic: "{{unselectedbgtopic}}"
condition:
- condition: template
value_template: "{{ is_state(haspsensor, 'ON') }}"
action:
- choose:
#########################################################################
# RUN ACTIONS or Home Assistant Startup or HASPone Connect
# Apply text style
- conditions:
- condition: template
value_template: >-
{{-
(trigger is not defined)
or
(trigger.platform is none)
or
((trigger.platform == 'homeassistant') and (trigger.event == 'start'))
or
((trigger.platform == 'template') and (trigger.entity_id == haspsensor) and (trigger.to_state.state == 'ON'))
-}}
sequence:
- repeat:
count: "{{objects|length}}"
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: >-
[
"{{objects[repeat.index-1]}}.pco={{selectedfg}}",
"{{objects[repeat.index-1]}}.bco={{selectedbg}}",
"{{objects[repeat.index-1]}}.pco2={{unselectedfg}}",
"{{objects[repeat.index-1]}}.bco2={{unselectedbg}}"
]
#########################################################################
# Catch triggers fired by incoming MQTT messages
- conditions:
- condition: template
value_template: '{{ trigger.platform == "mqtt" }}'
sequence:
- choose:
#########################################################################
# Theme: Apply selected foreground color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == selectedfgtopic) and ((selected_fgcolor|int) == -1) }}"
sequence:
- repeat:
count: "{{objects|length}}"
sequence:
- service: mqtt.publish
data:
topic: '{{ "hasp/" ~ haspname ~ "/command/" ~ objects[repeat.index-1] ~ ".pco" }}'
payload: "{{trigger.payload}}"
#########################################################################
# Theme: Apply selected background color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == selectedbgtopic) and ((selected_bgcolor|int) == -1) }}"
sequence:
- repeat:
count: "{{objects|length}}"
sequence:
- service: mqtt.publish
data:
topic: '{{ "hasp/" ~ haspname ~ "/command/" ~ objects[repeat.index-1] ~ ".bco" }}'
payload: "{{trigger.payload}}"
#########################################################################
# Theme: Apply unselected foreground color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == unselectedfgtopic) and ((unselected_fgcolor|int) == -1) }}"
sequence:
- repeat:
count: "{{objects|length}}"
sequence:
- service: mqtt.publish
data:
topic: '{{ "hasp/" ~ haspname ~ "/command/" ~ objects[repeat.index-1] ~ ".pco2" }}'
payload: "{{trigger.payload}}"
#########################################################################
# Theme: Apply unselected background color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == unselectedbgtopic) and ((unselected_bgcolor|int) == -1) }}"
sequence:
- repeat:
count: "{{objects|length}}"
sequence:
- service: mqtt.publish
data:
topic: '{{ "hasp/" ~ haspname ~ "/command/" ~ objects[repeat.index-1] ~ ".bco2" }}'
payload: "{{trigger.payload}}"

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone Core functionality"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description
@ -234,15 +234,15 @@ variables:
{%- endfor -%}
page1text: !input page1text
page1font_select: !input page1font_select
page1font: "{{ page1font_select.split(' - ')[0] | int }}"
page1font: "{{ page1font_select.split(' - ')[0] | int(default=6) }}"
page1page: !input page1page
page2text: !input page2text
page2font_select: !input page2font_select
page2font: "{{ page2font_select.split(' - ')[0] | int }}"
page2font: "{{ page2font_select.split(' - ')[0] | int(default=6) }}"
page2page: !input page2page
page3text: !input page3text
page3font_select: !input page3font_select
page3font: "{{ page3font_select.split(' - ')[0] | int }}"
page3font: "{{ page3font_select.split(' - ')[0] | int(default=6) }}"
page3page: !input page3page
page_scroll: !input page_scroll
page_scroll_list: !input page_scroll_list
@ -289,29 +289,29 @@ variables:
page_list: '{{page_scroll_list.split(",")}}'
page_previous: >
{%- set page = namespace() -%}
{%- set page.previous = page_list[(page_list|length)-1]|int -%}
{%- set page.next = page_list[0]|int -%}
{%- set page.previous = page_list[(page_list|length)-1]|int(default=10) -%}
{%- set page.next = page_list[0]|int(default=1) -%}
{%- for item in page_list -%}
{%- if item|int == activepage -%}
{%- if item|int(default=1) == activepage -%}
{%- if not loop.first -%}
{%- set page.previous = loop.previtem|int -%}
{%- set page.previous = loop.previtem|int(default=1) -%}
{%- endif -%}
{%- if not loop.last -%}
{%- set page.next = loop.nextitem|int -%}
{%- set page.next = loop.nextitem|int(default=1) -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}{{page.previous}}
page_next: >
{%- set page = namespace() -%}
{%- set page.previous = page_list[(page_list|length)-1]|int -%}
{%- set page.next = page_list[0]|int -%}
{%- set page.previous = page_list[(page_list|length)-1]|int(default=10) -%}
{%- set page.next = page_list[0]|int(default=1) -%}
{%- for item in page_list -%}
{%- if item|int == activepage -%}
{%- if item|int(default=1) == activepage -%}
{%- if not loop.first -%}
{%- set page.previous = loop.previtem|int -%}
{%- set page.previous = loop.previtem|int(default=1) -%}
{%- endif -%}
{%- if not loop.last -%}
{%- set page.next = loop.nextitem|int -%}
{%- set page.next = loop.nextitem|int(default=1) -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}{{page.next}}
@ -608,11 +608,11 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=0) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- else -%}{%- for p in range(1,12) %}"p[{{p}}].b[1].pco2={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- else -%}{%- for p in range(1,12) %}"p[{{p}}].b[3].pco2={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
- service: mqtt.publish # apply selected background color to page select buttons
data:
@ -628,11 +628,11 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=65535) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[1].bco2={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[3].bco2={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
- service: mqtt.publish # apply unselected foreground color to page select buttons
data:
@ -648,11 +648,11 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=59164) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[1].pco={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[3].pco={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
- service: mqtt.publish # apply unselected background color to page select buttons
data:
@ -668,11 +668,11 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=16904) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[1].bco={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[3].bco={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
#########################################################################
@ -707,11 +707,12 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=0) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- else -%}{%- for p in range(1,12) %}"p[{{p}}].b[1].pco2={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- else -%}{%- for p in range(1,12) %}"p[{{p}}].b[2].pco={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].pco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- else -%}{%- for p in range(1,12) %}"p[{{p}}].b[3].pco2={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
- service: mqtt.publish # apply selected background color to page select buttons
data:
@ -727,11 +728,12 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=65535) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[1].bco2={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco2={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[2].bco={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].bco={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco2={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[3].bco2={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
- service: mqtt.publish # apply unselected foreground color to page select buttons
data:
@ -747,11 +749,12 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=59164) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[1].pco={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].pco={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[2].pco2={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].pco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].pco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[3].pco={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
- service: mqtt.publish # apply unselected background color to page select buttons
data:
@ -767,11 +770,12 @@ action:
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- set colorcode = (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int(default=16904) -%}
[{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[1].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[1].bco={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[2].bco={{colorcode}}"{%- endif -%},{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[2].bco2={{colorcode}}",{%- endfor -%}{%- endif -%}
{%- if not page_scroll -%}{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].bco2={{colorcode}}"{%- else -%}"p[{{p}}].b[3].bco={{colorcode}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}
{% else %}{%- for p in range(1,12) %}"p[{{p}}].b[3].bco={{colorcode}}"{% if not loop.last %},{% endif %}{%- endfor -%}{%- endif -%}]
- service: mqtt.publish # request sensor update
data:
@ -847,9 +851,9 @@ action:
data:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].pco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].pco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].pco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].pco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].pco2={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
[{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].pco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].pco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].pco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].pco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].pco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].pco2={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
- conditions:
- condition: template
value_template: "{{ (trigger.platform == 'mqtt') and (trigger.topic == selectedfgtopic) and page_scroll }}"
@ -859,7 +863,7 @@ action:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}"p[{{p}}].b[1].pco2={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].pco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[2].pco={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[3].pco2={{trigger.payload}}"{% if not loop.last %},{% endif %}{%- endfor -%}]
- conditions:
- condition: template
@ -869,9 +873,9 @@ action:
data:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].bco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].bco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].bco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].bco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].bco2={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
[{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].bco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].bco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].bco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].bco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].bco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].bco2={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
- conditions:
- condition: template
value_template: "{{ (trigger.platform == 'mqtt') and (trigger.topic == selectedbgtopic) and page_scroll }}"
@ -881,7 +885,7 @@ action:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}"p[{{p}}].b[1].bco2={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].bco2={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[2].bco={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[3].bco2={{trigger.payload}}"{% if not loop.last %},{% endif %}{%- endfor -%}]
- conditions:
- condition: template
@ -891,9 +895,9 @@ action:
data:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].pco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].pco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].pco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].pco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].pco={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
[{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].pco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].pco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].pco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].pco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].pco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].pco={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
- conditions:
- condition: template
value_template: "{{ (trigger.platform == 'mqtt') and (trigger.topic == unselectedfgtopic) and page_scroll }}"
@ -903,7 +907,7 @@ action:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}"p[{{p}}].b[1].pco={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].pco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].pco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[2].pco2={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[3].pco={{trigger.payload}}"{% if not loop.last %},{% endif %}{%- endfor -%}]
- conditions:
- condition: template
@ -913,9 +917,9 @@ action:
data:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}{%- if p == page1page|int %}"p[{{p}}].b[1].bco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].bco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].bco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int %}"p[{{p}}].b[3].bco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].bco={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
[{%- for p in range(1,12) %}{%- if p == page1page|int(default=1) %}"p[{{p}}].b[1].bco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[1].bco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int(default=2) %}"p[{{p}}].b[2].bco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].bco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page3page|int(default=3) %}"p[{{p}}].b[3].bco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[3].bco={{trigger.payload}}"{%- endif -%}{% if not loop.last %},{% endif %}{%- endfor -%}]
- conditions:
- condition: template
value_template: "{{ (trigger.platform == 'mqtt') and (trigger.topic == unselectedbgtopic) and page_scroll }}"
@ -925,5 +929,5 @@ action:
topic: "{{jsoncommandtopic}}"
payload: >-
[{%- for p in range(1,12) %}"p[{{p}}].b[1].bco={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}{%- if p == page2page|int %}"p[{{p}}].b[2].bco2={{trigger.payload}}"{%- else -%}"p[{{p}}].b[2].bco={{trigger.payload}}"{%- endif -%},{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[2].bco2={{trigger.payload}}",{%- endfor -%}
{%- for p in range(1,12) %}"p[{{p}}].b[3].bco={{trigger.payload}}"{% if not loop.last %},{% endif %}{%- endfor -%}]

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone create device triggers"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] cycles through multiple automations"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone dim the display screen after a specified period of inactivity"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone dims the backlight with the sun"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[7].b[all] displays an alarm control panel"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the month + date with a calendar icon"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays a clock"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -165,11 +165,11 @@ variables:
hasppage: !input hasppage
haspbutton: !input haspbutton
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=8) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
hour24: !input hour24
ampm: !input ampm
wrap: !input wrap

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays a clock with a clock icon"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays a dimmer with a toggle on/off icon"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -144,11 +144,11 @@ variables:
text_on: !input text_on
text_off: !input text_off
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=6) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
wrap: !input wrap
icon_on: !input icon_on
icon_off: !input icon_off

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the state or attribute value of an entity"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -239,11 +239,11 @@ variables:
prefix: !input prefix
suffix: !input suffix
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=8) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
wrap: !input wrap
title_case: !input title_case
selected_fgcolor: !input selected_fgcolor
@ -474,4 +474,3 @@ action:
data:
topic: "{{commandtopic}}.bco2"
payload: "{{trigger.payload}}"

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[6].b[all] Page 6 displays Heatpump controls"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[9].b[all] Page 9 displays Heatpump controls"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[8].b[all] Page 8 displays media controls"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the output of a template"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -134,7 +134,7 @@ blueprint:
description: "Enter a well-formed [Home Assistant template](https://www.home-assistant.io/docs/configuration/templating/) string. The variable `trigger_entity` will contain the entity name selected above."
default: 'Forecast: {{state_attr("weather.home", "forecast")[0].condition|title}}'
selector:
text:
template:
font_select:
name: "Font"
description: "Select the font for the displayed text. Refer to the HASPone Font Reference above."
@ -232,11 +232,11 @@ variables:
trigger_entity: !input trigger_entity
text: !input template
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=8) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
wrap: !input wrap
selected_fgcolor: !input selected_fgcolor
selected_bgcolor: !input selected_bgcolor

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays text"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description
@ -215,11 +215,11 @@ variables:
haspbutton: !input haspbutton
text: !input text
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=8) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
wrap: !input wrap
selected_fgcolor: !input selected_fgcolor
selected_bgcolor: !input selected_bgcolor

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays a toggle button"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -268,6 +268,12 @@ blueprint:
default: false
selector:
boolean:
text_enable:
name: "Text enabled"
description: "Enable text, font, and colors. If disabled, no output will be sent to the button but the toggle actions will still be activated on press. Useful to combine with other blueprints that might place output on this button."
default: true
selector:
boolean:
mode: parallel
max_exceeded: silent
@ -294,12 +300,13 @@ variables:
off_fgcolor: !input off_fgcolor
off_bgcolor: !input off_bgcolor
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=8) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
wrap: !input wrap
text_enable: !input text_enable
haspobject: '{{ "p[" ~ hasppage ~ "].b[" ~ haspbutton ~ "]" }}'
commandtopic: '{{ "hasp/" ~ haspname ~ "/command/" ~ haspobject }}'
jsontopic: '{{ "hasp/" ~ haspname ~ "/state/json" }}'
@ -429,6 +436,8 @@ action:
or
((trigger.platform == 'template') and (trigger.entity_id == haspsensor) and (trigger.to_state.state == 'ON'))
-}}
sequence:
- service: mqtt.publish
data:
@ -450,7 +459,7 @@ action:
# Update display if our entity has changed state
- conditions: # Update display if our entity has changed state
- condition: template
value_template: '{{ (trigger.platform == "state") and (trigger.entity_id == entity) }}'
value_template: '{{ (trigger.platform == "state") and (trigger.entity_id == entity) and (text_enable == true) }}'
sequence:
- service: mqtt.publish
data:
@ -493,7 +502,7 @@ action:
# Theme: Apply selected foreground color when it changes
- conditions:
- condition: template
value_template: "{{ trigger.topic == selectedfgtopic }}"
value_template: "{{ (trigger.topic == selectedfgtopic) and (text_enable == true) }}"
sequence:
- service: mqtt.publish
data:
@ -503,7 +512,7 @@ action:
# Theme: Apply selected background color on change
- conditions:
- condition: template
value_template: "{{ trigger.topic == selectedbgtopic }}"
value_template: "{{ (trigger.topic == selectedbgtopic) and (text_enable == true) }}"
sequence:
- service: mqtt.publish
data:
@ -513,7 +522,7 @@ action:
# Theme: Apply unselected foreground color on change
- conditions:
- condition: template
value_template: "{{ trigger.topic == unselectedfgtopic }}"
value_template: "{{ (trigger.topic == unselectedfgtopic) and (text_enable == true) }}"
sequence:
- service: mqtt.publish
data:
@ -523,7 +532,7 @@ action:
# Theme: Apply unselected background color on change
- conditions:
- condition: template
value_template: "{{ trigger.topic == unselectedbgtopic }}"
value_template: "{{ (trigger.topic == unselectedbgtopic) and (text_enable == true) }}"
sequence:
- service: mqtt.publish
data:

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the value of a given entity with icons and colors"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[8].b[9] The slider button on page 8 displays a volume control"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the current weather condition"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -214,11 +214,11 @@ variables:
haspbutton: !input haspbutton
weather_provider: !input weather_provider
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=8) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
wrap: !input wrap
selected_fgcolor: !input selected_fgcolor
selected_bgcolor: !input selected_bgcolor

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the current weather condition icon only"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -183,11 +183,11 @@ variables:
haspbutton: !input haspbutton
weather_provider: !input weather_provider
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
font: '{{ font_select.split(" - ")[0] | int(default=8) }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
xcen: '{{ xcen_select.split(" - ")[0] | int(default=1) }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
ycen: '{{ ycen_select.split(" - ")[0] | int(default=1) }}'
selected_fgcolor: !input selected_fgcolor
selected_bgcolor: !input selected_bgcolor
unselected_fgcolor: !input unselected_fgcolor

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the current weather condition with icons"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the weather forecast"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description
@ -120,19 +120,29 @@ blueprint:
selector:
entity:
domain: weather
forecast_interval:
name: "Forecast interval"
description: 'Forecast interval, one of "hourly", "twice daily", or "daily". Not all weather providers will offer all options.'
default: "daily"
selector:
select:
options:
- "hourly"
- "twice_daily"
- "daily"
forecast_index:
name: "Forecast index"
description: 'Weather forecasts are provided at intervals determined by your weather source. The next time interval will be index "0". Increment this number for future forecasts'
description: 'Select a specific forecast, the next time interval will be index "0". Increment this number for future forecasts'
default: 0
selector:
number:
min: 0
max: 10
max: 48
mode: slider
unit_of_measurement: index
forecast_attribute:
name: "Enter the desired forecast attribute"
description: 'Type in the name of the desired forecast attribute for your provider. "condition" is a common attribute for many providers.'
description: 'Type in the name of the desired forecast attribute for your provider. "condition" is a common attribute for many providers.'
default: "condition"
selector:
text:
@ -243,6 +253,7 @@ variables:
hasppage: !input hasppage
haspbutton: !input haspbutton
weather_provider: !input weather_provider
forecast_interval: !input forecast_interval
forecast_index: !input forecast_index
forecast_attribute: !input forecast_attribute
prefix: !input prefix
@ -261,15 +272,6 @@ variables:
haspobject: '{{ "p[" ~ hasppage ~ "].b[" ~ haspbutton ~ "]" }}'
commandtopic: '{{ "hasp/" ~ haspname ~ "/command/" ~ haspobject }}'
jsoncommandtopic: '{{ "hasp/" ~ haspname ~ "/command/json" }}'
text: >-
{%- if prefix|lower != "none" -%}
{{ prefix }}
{%- endif -%}
{%- if title_case -%}
{{ state_attr(weather_provider, "forecast")[forecast_index|int(default=0)].get(forecast_attribute)|replace("windy-variant","windy")|replace("clear-night","clear night")|replace("partlycloudy","partly cloudy")|replace("lightning-rainy","lightning & rain")|replace("snowy-rainy","snow & rain") | title }}
{%- else -%}
{{ state_attr(weather_provider, "forecast")[forecast_index|int(default=0)].get(forecast_attribute)|replace("windy-variant","windy")|replace("clear-night","clear night")|replace("partlycloudy","partly cloudy")|replace("lightning-rainy","lightning & rain")|replace("snowy-rainy","snow & rain") }}
{%- endif -%}
isbr: "{% if wrap == true %}1{% else %}0{% endif %}"
selectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedforegroundcolor/rgb" }}'
selectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedbackgroundcolor/rgb" }}'
@ -380,6 +382,23 @@ condition:
value_template: "{{ is_state(haspsensor, 'ON') }}"
action:
- service: weather.get_forecasts
target:
entity_id: !input weather_provider
data:
type: "{{forecast_interval}}"
response_variable: weather_forecast
- variables:
text: >-
{%- if prefix|lower != "none" -%}
{{ prefix }}
{%- endif -%}
{%- if title_case -%}
{{ weather_forecast[weather_provider]['forecast'][forecast_index][forecast_attribute]|replace("windy-variant","windy")|replace("clear-night","clear night")|replace("partlycloudy","partly cloudy")|replace("lightning-rainy","lightning & rain")|replace("snowy-rainy","snow & rain") | title }}
{%- else -%}
{{ weather_forecast[weather_provider]['forecast'][forecast_index][forecast_attribute]|replace("windy-variant","windy")|replace("clear-night","clear night")|replace("partlycloudy","partly cloudy")|replace("lightning-rainy","lightning & rain")|replace("snowy-rainy","snow & rain") }}
{%- endif -%}
- choose:
#########################################################################
# Display attribute and apply text style when "RUN ACTIONS" is pressed by the user

View File

@ -0,0 +1,459 @@
blueprint:
name: "HASPone p[x].b[y] displays the weather forecast High and Low temperature"
description: |
## Blueprint Version: `1.06.00`
## Description
A HASPone button displays the high and low temperatures from a selected weather forecast.
![Preview](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/hasp_Display_Weather_Forecast.png)
## HASPone Page and Button Reference
The images below show each available HASPone page along with the layout of available button objects.
<details>
| Page 0 | Pages 1-3 | Pages 4-5 |
|--------|-----------|-----------|
| ![Page 0](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p0_Init_Screen.png) | ![Pages 1-3](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p1-p3_4buttons.png) | ![Pages 4-5](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p4-p5_3sliders.png) |
| Page 6 | Page 7 | Page 8 |
|--------|--------|--------|
| ![Page 6](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p6_8buttons.png) | ![Page 7](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p7_12buttons.png) | ![Page 8](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p8_5buttons+1slider.png) |
| Page 9 | Page 10 | Page 11 |
|--------|---------|---------|
| ![Page 9](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p9_9buttons.png) | ![Page 10](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p10_5buttons.png) | ![Page 11](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p11_1button+1slider.png)
</details>
## HASPone Font Reference
<details>
The Nextion display supports monospaced and proportional fonts. For monospace fonts, the HASPone project includes [Consolas](https://docs.microsoft.com/en-us/typography/font-list/consolas) monospace in 4 sizes, [Webdings](https://en.wikipedia.org/wiki/Webdings#Character_set) in 1 size, and [Google's "Noto Sans"](https://github.com/googlefonts/noto-fonts) proportional in 5 sizes
| Font | Name | Characters per line | Lines per button |
| :--- | :---------------- | :-------------------| :--------------- |
| 0 | Consolas 24 | 20 characters | 2 lines |
| 1 | Consolas 32 | 15 characters | 2 lines |
| 2 | Consolas 48 | 10 characters | 1 line |
| 3 | Consolas 80 | 6 characters | 1 line |
| 4 | Webdings 56 | 8 characters | 1 line |
| 5 | Noto Sans 24 | Proportional | 2 lines |
| 6 | Noto Sans 32 | Proportional | 2 lines |
| 7 | Noto Sans 48 | Proportional | 1 line |
| 8 | Noto Sans 64 | Proportional | 1 line |
| 9 | Noto Sans 80 | Proportional | 1 line |
| 10 | Noto Sans Bold 80 | Proportional | 1 line |
### Icons
Fonts 5-10 also include [1400+ icons which you can copy and paste from here](https://htmlpreview.github.io/?https://github.com/HASwitchPlate/HASPone/blob/main/images/hasp-fontawesome5.html)
### Font examples
![HASPone Fonts 0-3](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_Fonts_0-3.png) ![HASPone Fonts 4-7](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_Fonts_4-7.png) ![HASPone Fonts 8-10](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_Fonts_8-10.png)
</details>
## Nextion color codes
<details>
The Nextion environment utilizes RGB 565 encoding. [Use this handy convertor](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to select your colors and convert to the RGB 565 format.
Here are some example colors:
| Color | Code |
|--------|-------|
| White | 65535 |
| Black | 0 |
| Grey | 25388 |
| Red | 63488 |
| Green | 2016 |
| Blue | 31 |
| Yellow | 65504 |
| Orange | 64512 |
| Brown | 48192 |
</details>
domain: automation
input:
haspdevice:
name: "HASPone Device"
description: "Select the HASPone device"
selector:
device:
integration: mqtt
manufacturer: "HASwitchPlate"
model: "HASPone v1.0.0"
hasppage:
name: "HASPone Page"
description: "Select the HASPone page (1-11) for the forecast. Refer to the HASPone Page and Button reference above."
default: 1
selector:
number:
min: 1
max: 11
mode: slider
unit_of_measurement: page
haspbutton:
name: "HASPone Button"
description: "Select the HASPone button (4-15) for the forecast. Refer to the HASPone Page and Button reference above."
default: 4
selector:
number:
min: 4
max: 15
mode: slider
unit_of_measurement: button
weather_provider:
name: "Weather provider"
description: "Select the weather provider to obtain the forecast"
selector:
entity:
domain: weather
forecast_interval:
name: "Forecast interval"
description: 'Forecast interval, one of "hourly", "twice daily", or "daily". Not all weather providers will offer all options.'
default: "daily"
selector:
select:
options:
- "hourly"
- "twice_daily"
- "daily"
forecast_index:
name: "Forecast index"
description: 'Select a specific forecast, the next time interval will be index "0". Increment this number for future forecasts'
default: 0
selector:
number:
min: 0
max: 48
mode: slider
unit_of_measurement: index
font_select:
name: "Font"
description: "Select the font for the displayed text. You probably want to leave this as 10, refer to the HASPone Font Reference above."
default: "10 - Noto Sans Bold 80"
selector:
select:
options:
- "0 - Consolas 24"
- "1 - Consolas 32"
- "2 - Consolas 48"
- "3 - Consolas 80"
- "4 - Webdings 56"
- "5 - Noto Sans 24"
- "6 - Noto Sans 32"
- "7 - Noto Sans 48"
- "8 - Noto Sans 64"
- "9 - Noto Sans 80"
- "10 - Noto Sans Bold 80"
xcen_select:
name: "Text horizontal alignment"
description: "Horizontal text alignment: 0=Left 1=Center 2=Right"
default: "1 - Centered"
selector:
select:
options:
- "0 - Left aligned"
- "1 - Centered"
- "2 - Right aligned"
ycen_select:
name: "Text vertical alignment"
description: "Vertical text alignment: 0=Top 1=Center 2=Bottom"
default: "1 - Centered"
selector:
select:
options:
- "0 - Top aligned"
- "1 - Centered"
- "2 - Bottom aligned"
wrap:
name: "Text wrap"
default: false
description: "Enable line-wrapping text if too long to fit in the button."
selector:
boolean:
selected_fgcolor:
name: "Selected foreground color"
description: 'Selected foreground color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme selected foreground color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
selected_bgcolor:
name: "Selected background color"
description: 'Selected background color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme selected background color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
unselected_fgcolor:
name: "Unselected foreground color"
description: 'Unselected foreground color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme unselected foreground color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
unselected_bgcolor:
name: "Unselected background color"
description: 'Unselected background color in Nextion RGB565 format (see "Nextion color codes" above for reference). -1 = Current theme unselected background color.'
default: -1
selector:
number:
min: -1
max: 65535
mode: slider
mode: parallel
max_exceeded: silent
variables:
haspdevice: !input haspdevice
haspname: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{- entity|regex_replace(find="^sensor\.", replace="", ignorecase=true)|regex_replace(find="_sensor(?:_\d+|)$", replace="", ignorecase=true) -}}
{%- endif -%}
{%- endfor -%}
hasppage: !input hasppage
haspbutton: !input haspbutton
weather_provider: !input weather_provider
forecast_interval: !input forecast_interval
forecast_index: !input forecast_index
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
wrap: !input wrap
selected_fgcolor: !input selected_fgcolor
selected_bgcolor: !input selected_bgcolor
unselected_fgcolor: !input unselected_fgcolor
unselected_bgcolor: !input unselected_bgcolor
haspobject: '{{ "p[" ~ hasppage ~ "].b[" ~ haspbutton ~ "]" }}'
commandtopic: '{{ "hasp/" ~ haspname ~ "/command/" ~ haspobject }}'
jsoncommandtopic: '{{ "hasp/" ~ haspname ~ "/command/json" }}'
selectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedforegroundcolor/rgb" }}'
selectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedbackgroundcolor/rgb" }}'
unselectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedforegroundcolor/rgb" }}'
unselectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedbackgroundcolor/rgb" }}'
selectedfg: >-
{%- if (selected_fgcolor|int) >= 0 -%}
{{ selected_fgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_selected_foreground_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
selectedbg: >-
{%- if (selected_bgcolor|int) >= 0 -%}
{{ selected_bgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_selected_background_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
unselectedfg: >-
{%- if (unselected_fgcolor|int) >= 0 -%}
{{ unselected_fgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_unselected_foreground_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
unselectedbg: >-
{%- if (unselected_bgcolor|int) >= 0 -%}
{{ unselected_bgcolor }}
{%- else -%}
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_unselected_background_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
{%- endif -%}
trigger_variables:
haspdevice: !input haspdevice
haspname: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{- entity|regex_replace(find="^sensor\.", replace="", ignorecase=true)|regex_replace(find="_sensor(?:_\d+|)$", replace="", ignorecase=true) -}}
{%- endif -%}
{%- endfor -%}
haspsensor: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{ entity }}
{%- endif -%}
{%- endfor -%}
selectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedforegroundcolor/rgb" }}'
selectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedbackgroundcolor/rgb" }}'
unselectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedforegroundcolor/rgb" }}'
unselectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedbackgroundcolor/rgb" }}'
trigger:
- platform: state
entity_id: !input weather_provider
- platform: template
value_template: "{{ is_state(haspsensor, 'ON') }}"
- platform: homeassistant
event: start
- platform: mqtt
topic: "{{selectedfgtopic}}"
- platform: mqtt
topic: "{{selectedbgtopic}}"
- platform: mqtt
topic: "{{unselectedfgtopic}}"
- platform: mqtt
topic: "{{unselectedbgtopic}}"
condition:
- condition: template
value_template: "{{ is_state(haspsensor, 'ON') }}"
action:
- service: weather.get_forecasts
target:
entity_id: !input weather_provider
data:
type: "{{forecast_interval}}"
response_variable: weather_forecast
- variables:
temphigh: '{{ weather_forecast[weather_provider]["forecast"][forecast_index]["temperature"] }}'
templow: '{{ weather_forecast[weather_provider]["forecast"][forecast_index]["templow"] }}'
text: "{{templow|int(default=0)}}° {{temphigh|int(default=0)}}°"
- choose:
#########################################################################
# Display attribute and apply text style when "RUN ACTIONS" is pressed by the user
- conditions:
- condition: template
value_template: >-
{{-
(trigger is not defined)
or
(trigger.platform is none)
or
((trigger.platform == 'homeassistant') and (trigger.event == 'start'))
or
((trigger.platform == 'template') and (trigger.entity_id == haspsensor) and (trigger.to_state.state == 'ON'))
-}}
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: >-
[
"{{haspobject}}.font={{font}}",
"{{haspobject}}.xcen={{xcen}}",
"{{haspobject}}.ycen={{ycen}}",
"{{haspobject}}.isbr={{isbr}}",
"{{haspobject}}.pco={{selectedfg}}",
"{{haspobject}}.bco={{selectedbg}}",
"{{haspobject}}.pco2={{unselectedfg}}",
"{{haspobject}}.bco2={{unselectedbg}}",
"{{haspobject}}.txt=\"{{text}}\""
]
#########################################################################
# Update forecast if our weather provider changed state
- conditions:
- condition: template
value_template: '{{ (trigger.platform == "state") and (trigger.entity_id == weather_provider) }}'
sequence:
- service: mqtt.publish
data:
topic: "{{commandtopic}}.txt"
payload: '"{{text}}"'
#########################################################################
# Catch triggers fired by incoming MQTT messages
- conditions:
- condition: template
value_template: '{{ trigger.platform == "mqtt" }}'
sequence:
- choose:
#########################################################################
# Theme: Apply selected foreground color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == selectedfgtopic) and ((selected_fgcolor|int) == -1) }}"
sequence:
- service: mqtt.publish
data:
topic: "{{commandtopic}}.pco"
payload: "{{trigger.payload}}"
#########################################################################
# Theme: Apply selected background color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == selectedbgtopic) and ((selected_bgcolor|int) == -1) }}"
sequence:
- service: mqtt.publish
data:
topic: "{{commandtopic}}.bco"
payload: "{{trigger.payload}}"
#########################################################################
# Theme: Apply unselected foreground color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == unselectedfgtopic) and ((unselected_fgcolor|int) == -1) }}"
sequence:
- service: mqtt.publish
data:
topic: "{{commandtopic}}.pco2"
payload: "{{trigger.payload}}"
#########################################################################
# Theme: Apply unselected background color on change
- conditions:
- condition: template
value_template: "{{ (trigger.topic == unselectedbgtopic) and ((unselected_bgcolor|int) == -1) }}"
sequence:
- service: mqtt.publish
data:
topic: "{{commandtopic}}.bco2"
payload: "{{trigger.payload}}"

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the current temperature from a weather provider, coloured icon only"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -201,7 +201,7 @@ variables:
jsoncommandtopic: '{{ "hasp/" ~ haspname ~ "/command/json" }}'
temperature: '{{ state_attr(weather_provider, "temperature") }}'
icon: >-
{%- set temp = temperature|int -%}
{%- set temp = temperature|int(default=0) -%}
{%- if temp <= thermometer_quarter_threshold|int -%}
{%- elif temp < thermometer_half_threshold|int -%}

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] displays the current temperature from a weather provider with icon and colors"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description
@ -224,10 +224,10 @@ variables:
{%- if roundtemp == true -%}
{{- state_attr(weather_provider, "temperature") | round(default=0) -}}
{%- else -%}
{{- state_attr(weather_provider, "temperature") -}}
{{- state_attr(weather_provider, "temperature") | float(default=0) -}}
{%- endif -%}
icon: >-
{%- set temp = temperature|int -%}
{%- set temp = temperature|int(default=0) -%}
{%- if temp <= thermometer_quarter_threshold|int -%}
{%- elif temp < thermometer_half_threshold|int -%}
@ -310,7 +310,7 @@ variables:
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
tempcolor: >-
{%- set temp = temperature|int -%}
{%- set temp = temperature|int(default=0) -%}
{%- if temp <= thermometer_quarter_threshold|int -%}
{%- set color = thermometer_empty_color -%}
{%- elif temp < thermometer_half_threshold|int -%}

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone p[x].b[y] performs an action when pressed"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone Remove MQTT discovery messages"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
# Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone Theme Dark on Light"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone Theme Light on Dark Blue"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description
@ -52,7 +52,7 @@ variables:
{%- endfor -%}
selected_foreground_brightness: "255"
selected_foreground_color: "[255, 255, 255]"
selected_background_brightness: "1"
selected_background_brightness: "32"
selected_background_color: "[0, 0, 255]"
unselected_foreground_brightness: "224"
unselected_foreground_color: "[255, 255, 255]"

View File

@ -2,7 +2,7 @@ blueprint:
name: "HASPone Theme Light on Dark"
description: |
## Blueprint Version: `1.05.00`
## Blueprint Version: `1.06.00`
## Description

75
esphome/haspone.yaml Normal file
View File

@ -0,0 +1,75 @@
# Example ESPhome configuration for use with the HASPone hardware
substitutions:
device_name: "haspone"
friendly_name: "HASPone hardware for ESPhome"
project_version: "0.0.1"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
comment: "http://haswitchplate.com"
project:
name: "esphome.${device_name}"
version: ${project_version}
on_boot:
then:
- switch.turn_on: switch_lcdpower # Power up the Nextion on boot
esp8266:
board: d1_mini
logger:
api:
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
web_server:
port: 80
# HASPone switch controls power to the Nextion. Set HIGH at power on to enable the device at boot.
switch:
- platform: gpio
id: switch_lcdpower
name: "${friendly_name} Nextion Power"
pin: D6 #GPIO12
restore_mode: ALWAYS_ON
internal: false
# UART for HASPone communication to Nextion. This will utilize software serial and might not work well for TFT updates.
uart:
id: uart_nextion
tx_pin: D4 #GPIO2
rx_pin: D7 #GPIO13
baud_rate: 115200
# Nextion display device
display:
- platform: nextion
id: display_nextion
uart_id: uart_nextion
on_touch:
then:
lambda: |-
ESP_LOGD("nextion.on_touch", "Nextion touch event detected!");
ESP_LOGD("nextion.on_touch", "Page Id: %i", page_id);
ESP_LOGD("nextion.on_touch", "Component Id: %i", component_id);
ESP_LOGD("nextion.on_touch", "Event type: %s", touch_event ? "Press" : "Release");
# Nextion backlight control
number:
- platform: template
id: number_brightness
name: "${friendly_name} Nextion Brightness"
min_value: 0
max_value: 100
step: 1
initial_value: 100
optimistic: true
set_action:
- lambda: id(display_nextion)->set_backlight_brightness(x/100);

3
esphome/readme.md Normal file
View File

@ -0,0 +1,3 @@
# HASPone for ESPhome
Here you'll find an example ESPhome configuration for use with the HASPone hardware, this should be compatible with existing ESPhome-native Nextion projects for a 2.8" panel.

BIN
images/HASP PCB Back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

BIN
images/HASP PCB Front.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View File

@ -1,18 +1,18 @@
{
"d1_mini": {
"version": "1.05",
"firmware": "https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/Arduino_Sketch/HASwitchPlate.ino.d1_mini.bin"
"version": "1.06",
"firmware": "https://haswitchplate.com/update/main/HASwitchPlate.ino.d1_mini.bin"
},
"NX3224T024_011R": {
"version": 3,
"firmware": "https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/Nextion_HMI/HASwitchPlate.tft"
"firmware": "https://haswitchplate.com/update/main/HASwitchPlate.tft"
},
"NX3224K024_011R": {
"version": 3,
"firmware": "https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/Nextion_HMI/HASwitchPlate-Enhanced.tft"
"firmware": "https://haswitchplate.com/update/main/HASwitchPlate-Enhanced.tft"
},
"NX3224F024_011R": {
"version": 3,
"firmware": "https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/Nextion_HMI/HASwitchPlate-Discovery.tft"
"firmware": "https://haswitchplate.com/update/main/HASwitchPlate-Discovery.tft"
}
}