mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
release 1.9.2
This commit is contained in:
33
CHANGELOG.md
33
CHANGELOG.md
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.9.2 beta]
|
||||
|
||||
#### Important! This build has breaking changes:
|
||||
- MQTT topics have changed. Use the `mqttlog` command to see the names of the subscriptions and the format of the payload data. Also reference the [Wiki page](https://github.com/proddy/EMS-ESP/wiki/MQTT).
|
||||
- Home Assistant `.yaml` files need updating to reflect the recent MQTT changes
|
||||
- The web builder has been upgraded to use Gulp 4. Remove `tools/webfilesbuilder/node_modules` and re-install the libraries using `npm ci` from within the `tools/webfilesbuilder` folder
|
||||
|
||||
### Added
|
||||
|
||||
- Handling of Mixing Module MM100 Status Messages (thanks @kstaniek)
|
||||
- Retrieve/Set thermostat mode for Junkers FW100/120 thermostats (thanks @Neonox31)
|
||||
- Added sending of all Mixer Module data via MQTT (thanks @peclik)
|
||||
- Improved handling of MQTT publish and subscribe errors
|
||||
- Added MQTT QOS (`mqtt_qos`, default 0), Keep Alive (`mqtt_keepalive`, default 60 seconds) and Retain (`mqtt_retain`, default off) as parameters to both telnet and WebUI
|
||||
|
||||
### Fixed
|
||||
|
||||
- `publish` command also sends Dallas external temperature sensor values
|
||||
- `log_events` setting wasn't persisted in config file
|
||||
|
||||
### Changed
|
||||
|
||||
- External dallas sensor values sent in MQTT payload as float values and not strings
|
||||
- All MQTT topics for the Thermostat have the Heating Circuit appended (e.g. `thermostat_data1`). This includes the commands.
|
||||
- Shower timer and shower alert and not MQTT published at boot up
|
||||
- Heating Active logic change to use Selected Flow Temp of min 30 instead of 70 (https://github.com/proddy/EMS-ESP/issues/193)
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed telnet command `shower timer` and `shower alert` to toggle the switches
|
||||
|
||||
## [1.9.1] 2019-10-05
|
||||
|
||||
### Added
|
||||
@@ -12,7 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Support for multiple Heating Circuits - https://github.com/proddy/EMS-ESP/issues/162
|
||||
- new `mqttlog` command also shows which MQTT topics it is subscribed too
|
||||
- Optimized event log loading in web and added integrity checks on all config and log files during boot
|
||||
- `autodetect quick`
|
||||
- `autodetect quick` for detecting known devices from our database list
|
||||
- `log_events` option, now optional to save the log events to SPIFFS
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -1,25 +1,14 @@
|
||||
- id: boiler_shower
|
||||
alias: Alert shower time
|
||||
initial_state: true
|
||||
trigger:
|
||||
platform: mqtt
|
||||
topic: home/ems-esp/showertime
|
||||
topic: home/ems-esp/shower_data
|
||||
action:
|
||||
- service: notify.general_notify
|
||||
data_template:
|
||||
title: "Shower finished at {{states.sensor.time.state}}"
|
||||
message: '{{trigger.payload}}'
|
||||
|
||||
- id: boiler_shower_alarm
|
||||
alias: Alert shower too long
|
||||
trigger:
|
||||
platform: mqtt
|
||||
topic: home/ems-esp/command
|
||||
payload: 'shower_alarm'
|
||||
action:
|
||||
- service: notify.admin_notify
|
||||
data_template:
|
||||
title: "Shower Alert!"
|
||||
message: "Shower time exceeded limit"
|
||||
- service: notify.admin_notify
|
||||
data_template:
|
||||
title: Shower finished at {{states.sensor.time.state}}
|
||||
message: "{{ trigger.payload_json['duration'] }}"
|
||||
|
||||
# when ems-esp starts send boottime
|
||||
- id: boiler_restart
|
||||
|
||||
@@ -1,37 +1,39 @@
|
||||
- platform: mqtt
|
||||
name: Thermostat
|
||||
modes:
|
||||
- "auto"
|
||||
- "heat"
|
||||
- "off"
|
||||
- platform: mqtt
|
||||
name: Thermostat
|
||||
modes:
|
||||
- "auto"
|
||||
- "heat"
|
||||
- "off"
|
||||
mode_command_topic: "home/ems-esp/thermostat_cmd_mode1"
|
||||
temperature_command_topic: "home/ems-esp/thermostat_cmd_temp1"
|
||||
|
||||
mode_state_topic: "home/ems-esp/thermostat_data"
|
||||
current_temperature_topic: "home/ems-esp/thermostat_data"
|
||||
temperature_state_topic: "home/ems-esp/thermostat_data"
|
||||
|
||||
mode_state_topic: "home/ems-esp/thermostat_data"
|
||||
current_temperature_topic: "home/ems-esp/thermostat_data"
|
||||
temperature_state_topic: "home/ems-esp/thermostat_data"
|
||||
mode_state_template: "{{ value_json.hc1.mode }}"
|
||||
current_temperature_template: "{{ value_json.hc1.currtemp }}"
|
||||
temperature_state_template: "{{ value_json.hc1.seltemp }}"
|
||||
|
||||
temp_step: 0.5
|
||||
|
||||
- platform: mqtt
|
||||
name: boiler
|
||||
modes:
|
||||
- "auto"
|
||||
- "off"
|
||||
min_temp: 40
|
||||
max_temp: 60
|
||||
temp_step: 1
|
||||
|
||||
current_temperature_topic: "home/ems-esp/boiler_data"
|
||||
temperature_state_topic: "home/ems-esp/boiler_data"
|
||||
mode_state_topic: "home/ems-esp/boiler_data"
|
||||
|
||||
temperature_command_topic: "home/ems-esp/thermostat_cmd_temp"
|
||||
mode_command_topic: "home/ems-esp/thermostat_cmd_mode"
|
||||
current_temperature_template: "{{ value_json.wWCurTmp }}"
|
||||
temperature_state_template: "{{ value_json.wWSelTemp }}"
|
||||
mode_state_template: "{% if value_json.wWActivated == 'off' %} off {% else %} auto {% endif %}"
|
||||
|
||||
mode_state_template: "{{ value_json.thermostat_mode }}"
|
||||
current_temperature_template: "{{ value_json.thermostat_currtemp }}"
|
||||
temperature_state_template: "{{ value_json.thermostat_seltemp }}"
|
||||
|
||||
temp_step: 0.5
|
||||
|
||||
- platform: mqtt
|
||||
name: boiler
|
||||
modes:
|
||||
- "auto"
|
||||
- "off"
|
||||
min_temp: 40
|
||||
max_temp: 60
|
||||
temp_step: 1
|
||||
current_temperature_topic: "home/ems-esp/boiler_data"
|
||||
temperature_state_topic: "home/ems-esp/boiler_data"
|
||||
temperature_command_topic: "home/ems-esp/boiler_cmd_wwtemp"
|
||||
current_temperature_template: "{{ value_json.wWCurTmp }}"
|
||||
temperature_state_template: "{{ value_json.wWSelTemp }}"
|
||||
mode_state_template: "{% if value_json.wWActivated == 'off' %} off {% else %} auto {% endif %}"
|
||||
mode_state_topic: "home/ems-esp/boiler_data"
|
||||
mode_command_topic: "home/ems-esp/wwactivated"
|
||||
|
||||
temperature_command_topic: "home/ems-esp/boiler_cmd_wwtemp"
|
||||
mode_command_topic: "home/ems-esp/boiler_cmd_wwactivated"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# ems-esp
|
||||
shower_coldshot:
|
||||
sequence:
|
||||
- service: mqtt.publish
|
||||
data_template:
|
||||
topic: 'home/ems-esp/shower_coldshot'
|
||||
payload: '1'
|
||||
|
||||
topic: 'home/ems-esp/generic_cmd'
|
||||
payload: '{cmd:"coldshot"}'
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
|
||||
# thermostat HC1
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/ems-esp/thermostat_data'
|
||||
name: 'Current Room Temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: "{{ value_json.thermostat_currtemp }}"
|
||||
value_template: "{{ value_json.hc1.currtemp }}"
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/ems-esp/thermostat_data'
|
||||
name: 'Current Set Temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: "{{ value_json.thermostat_seltemp }}"
|
||||
value_template: "{{ value_json.hc1.seltemp }}"
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/ems-esp/thermostat_data'
|
||||
name: 'Current Mode'
|
||||
value_template: "{{ value_json.thermostat_mode }}"
|
||||
value_template: "{{ value_json.hc1.mode }}"
|
||||
|
||||
# last time esp-esp was started
|
||||
- platform: template
|
||||
sensors:
|
||||
boiler_boottime:
|
||||
value_template: '{{ as_timestamp(states.automation.see_if_boiler_restarts.attributes.last_triggered) | timestamp_custom("%H:%M:%S %d/%m/%y") }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/ems-esp/showertime'
|
||||
name: 'Last shower duration'
|
||||
force_update: true
|
||||
# boiler
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/ems-esp/boiler_data'
|
||||
@@ -137,13 +131,20 @@
|
||||
unit_of_measurement: '%'
|
||||
value_template: '{{ value_json.pumpMod }}'
|
||||
|
||||
# shower time duration
|
||||
- platform: mqtt
|
||||
name: 'Last shower duration'
|
||||
state_topic: "home/ems-esp/shower_data"
|
||||
value_template: "{{ value_json.duration }}"
|
||||
force_update: true
|
||||
|
||||
- platform: template
|
||||
sensors:
|
||||
showertime_time:
|
||||
value_template: '{{ as_timestamp(states.sensor.last_shower_duration.last_updated) | int | timestamp_custom("%-I:%M %P on %a %-d %b") }}'
|
||||
value_template: '{{ as_timestamp(states.sensor.last_shower_duration.last_updated) | int | timestamp_custom("%-I:%M on %a %-d %b") }}'
|
||||
|
||||
boiler_updated:
|
||||
value_template: '{{ as_timestamp(states.sensor.boiler_temperature.last_updated) | timestamp_custom("%H:%M on %d/%b") }}'
|
||||
|
||||
|
||||
|
||||
boiler_boottime:
|
||||
value_template: '{{ as_timestamp(states.automation.see_if_boiler_restarts.attributes.last_triggered) | timestamp_custom("%H:%M:%S %d/%m/%y") }}'
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
# EMS-ESP
|
||||
- platform: mqtt
|
||||
name: "Shower Timer"
|
||||
state_topic: "home/ems-esp/shower_timer"
|
||||
command_topic: "home/ems-esp/shower_timer"
|
||||
payload_on: "1"
|
||||
payload_off: "0"
|
||||
optimistic: false
|
||||
qos: 1
|
||||
retain: false
|
||||
state_topic: "home/ems-esp/shower_data"
|
||||
value_template: "{{ value_json.timer }}"
|
||||
command_topic: "home/ems-esp/shower_data"
|
||||
payload_on: '{"timer":"1"}'
|
||||
payload_off: '{"timer":"0"}'
|
||||
state_on: "1"
|
||||
state_off: "0"
|
||||
|
||||
- platform: mqtt
|
||||
name: "Long Shower Alert"
|
||||
state_topic: "home/ems-esp/shower_alert"
|
||||
command_topic: "home/ems-esp/shower_alert"
|
||||
payload_on: "1"
|
||||
payload_off: "0"
|
||||
optimistic: false
|
||||
qos: 1
|
||||
retain: false
|
||||
|
||||
state_topic: "home/ems-esp/shower_data"
|
||||
value_template: "{{ value_json.alert }}"
|
||||
command_topic: "home/ems-esp/shower_data"
|
||||
payload_on: '{"alert":"1"}'
|
||||
payload_off: '{"alert":"0"}'
|
||||
state_on: "1"
|
||||
state_off: "0"
|
||||
|
||||
@@ -5,23 +5,23 @@
|
||||
[platformio]
|
||||
default_envs = release
|
||||
;default_envs = debug
|
||||
;default_envs = crash
|
||||
;default_envs = tests
|
||||
|
||||
[common]
|
||||
; build options are:
|
||||
; custom build options are:
|
||||
; -DMYESP_TIMESTAMP
|
||||
; -DTESTS
|
||||
; -DCRASH
|
||||
; -DFORCE_SERIAL
|
||||
; -DLOGICANALYZER
|
||||
custom_flags =
|
||||
|
||||
;general_flags = -g -w -DNO_GLOBAL_EEPROM -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -DBEARSSL_SSL_BASIC
|
||||
general_flags = -g -w -DNO_GLOBAL_EEPROM
|
||||
;general_flags =
|
||||
;general_flags = -DNO_GLOBAL_EEPROM -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -DBEARSSL_SSL_BASIC
|
||||
general_flags = -DNO_GLOBAL_EEPROM
|
||||
|
||||
[env]
|
||||
;board = esp12e
|
||||
; board = esp12e
|
||||
; board = nodemcu
|
||||
; board = nodemcu2
|
||||
board = d1_mini
|
||||
framework = arduino
|
||||
platform = espressif8266
|
||||
@@ -43,40 +43,22 @@ monitor_speed = 115200
|
||||
;upload_port = /dev/cu.wchusbserial14403
|
||||
;upload_port = /dev/cu.usbserial-1440
|
||||
|
||||
; uncomment next 2 lines for OTA
|
||||
;upload_protocol = espota
|
||||
;upload_port = ems-esp.local
|
||||
|
||||
[env:buildweb]
|
||||
extra_scripts = pre:scripts/buildweb.py
|
||||
|
||||
[env:tests]
|
||||
build_type = debug
|
||||
build_flags = ${common.general_flags} -DTESTS
|
||||
extra_scripts =
|
||||
pre:scripts/rename_fw.py
|
||||
pre:scripts/buildweb.py
|
||||
|
||||
[env:crash]
|
||||
build_type = debug
|
||||
build_flags = ${common.general_flags} -DNO_GLOBAL_EEPROM -DCRASH
|
||||
extra_scripts =
|
||||
pre:scripts/rename_fw.py
|
||||
pre:scripts/buildweb.py
|
||||
; comment next 2 lines is not using OTA
|
||||
upload_protocol = espota
|
||||
upload_port = ems-esp.local
|
||||
|
||||
[env:debug]
|
||||
build_type = debug
|
||||
build_flags = ${common.general_flags}
|
||||
build_flags = ${common.general_flags} ${common.custom_flags} -DTESTS
|
||||
extra_scripts =
|
||||
pre:scripts/rename_fw.py
|
||||
pre:scripts/buildweb.py
|
||||
|
||||
[env:clean]
|
||||
extra_scripts = pre:scripts/clean_fw.py
|
||||
|
||||
[env:release]
|
||||
build_flags = ${common.general_flags} -DNO_GLOBAL_EEPROM
|
||||
extra_scripts = pre:scripts/rename_fw.py
|
||||
build_flags = ${common.general_flags} ${common.custom_flags}
|
||||
extra_scripts =
|
||||
pre:scripts/rename_fw.py
|
||||
pre:scripts/buildweb.py
|
||||
|
||||
[env:checkcode]
|
||||
build_type = debug
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Import("env")
|
||||
|
||||
env.Execute("node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --cwd ./tools/webfilesbuilder")
|
||||
env.Execute("node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --silent --cwd ./tools/webfilesbuilder")
|
||||
|
||||
410
src/MyESP.cpp
410
src/MyESP.cpp
@@ -199,7 +199,10 @@ uint32_t MyESP::_getInitialFreeHeap() {
|
||||
|
||||
// called when WiFi is connected, and used to start OTA, MQTT
|
||||
void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
if ((code == MESSAGE_CONNECTED)) {
|
||||
if (code == MESSAGE_CONNECTED) {
|
||||
myDebug_P(PSTR("[WIFI] Connected to SSID %s (hostname: %s, IP: %s)"), WiFi.SSID().c_str(), _getESPhostname().c_str(), WiFi.localIP().toString().c_str());
|
||||
|
||||
/*
|
||||
myDebug_P(PSTR("[WIFI] SSID %s"), WiFi.SSID().c_str());
|
||||
myDebug_P(PSTR("[WIFI] CH %d"), WiFi.channel());
|
||||
myDebug_P(PSTR("[WIFI] RSSI %d"), WiFi.RSSI());
|
||||
@@ -209,10 +212,11 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
myDebug_P(PSTR("[WIFI] MASK %s"), WiFi.subnetMask().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] DNS %s"), WiFi.dnsIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] HOST %s"), _getESPhostname().c_str());
|
||||
*/
|
||||
|
||||
// start OTA
|
||||
ArduinoOTA.begin(); // moved to support esp32
|
||||
myDebug_P(PSTR("[OTA] listening to %s.local:%u"), ArduinoOTA.getHostname().c_str(), OTA_PORT);
|
||||
myDebug_P(PSTR("[OTA] Listening to firmware updates on %s.local:%u"), ArduinoOTA.getHostname().c_str(), OTA_PORT);
|
||||
|
||||
/*
|
||||
// show reason for the restart if any
|
||||
@@ -232,11 +236,11 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
|
||||
// if we don't want Serial anymore, turn it off
|
||||
if (!_general_serial) {
|
||||
myDebug_P(PSTR("[SYSTEM] Disabling serial port communication."));
|
||||
myDebug_P(PSTR("[SYSTEM] Disabling serial port communication"));
|
||||
SerialAndTelnet.flush(); // flush so all buffer is printed to serial
|
||||
setUseSerial(false);
|
||||
} else {
|
||||
myDebug_P(PSTR("[SYSTEM] Serial port communication is enabled."));
|
||||
myDebug_P(PSTR("[SYSTEM] Serial port communication is enabled"));
|
||||
}
|
||||
|
||||
_wifi_connected = true;
|
||||
@@ -263,11 +267,11 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
|
||||
// if we don't want Serial anymore, turn it off
|
||||
if (!_general_serial) {
|
||||
myDebug_P(PSTR("[SYSTEM] Disabling serial port communication."));
|
||||
myDebug_P(PSTR("[SYSTEM] Disabling serial port communication"));
|
||||
SerialAndTelnet.flush(); // flush so all buffer is printed to serial
|
||||
setUseSerial(false);
|
||||
} else {
|
||||
myDebug_P(PSTR("[SYSTEM] Serial port communication is enabled."));
|
||||
myDebug_P(PSTR("[SYSTEM] Serial port communication is enabled"));
|
||||
}
|
||||
|
||||
_wifi_connected = true;
|
||||
@@ -279,7 +283,7 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
}
|
||||
|
||||
if (code == MESSAGE_CONNECTING) {
|
||||
myDebug_P(PSTR("[WIFI] Connecting to %s"), parameter);
|
||||
myDebug_P(PSTR("[WIFI] Connecting to %s..."), parameter);
|
||||
_wifi_connected = false;
|
||||
}
|
||||
|
||||
@@ -365,18 +369,24 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
||||
}
|
||||
|
||||
// MQTT subscribe
|
||||
void MyESP::mqttSubscribe(const char * topic) {
|
||||
// returns false if failed
|
||||
bool MyESP::mqttSubscribe(const char * topic) {
|
||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||
char * topic_s = _mqttTopic(topic);
|
||||
|
||||
//char topic_s[MQTT_MAX_TOPIC_SIZE];
|
||||
//strlcpy(topic_s, _mqttTopic(topic), sizeof(topic_s));
|
||||
(void)mqttClient.subscribe(topic_s, _mqtt_qos);
|
||||
uint16_t packet_id = mqttClient.subscribe(topic_s, _mqtt_qos);
|
||||
// myDebug_P(PSTR("[MQTT] Subscribing to %s"), topic_s);
|
||||
|
||||
// add to mqtt log
|
||||
_addMQTTLog(topic_s, "", 2); // type of 2 means Subscribe. Has an empty payload for now
|
||||
if (packet_id) {
|
||||
// add to mqtt log
|
||||
_addMQTTLog(topic_s, "", 2); // type of 2 means Subscribe. Has an empty payload for now
|
||||
return true;
|
||||
} else {
|
||||
myDebug_P(PSTR("[MQTT] Error subscribing to %s, error %d"), _mqttTopic(topic), packet_id);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // didn't work
|
||||
}
|
||||
|
||||
// MQTT unsubscribe
|
||||
@@ -387,30 +397,47 @@ void MyESP::mqttUnsubscribe(const char * topic) {
|
||||
}
|
||||
}
|
||||
|
||||
// MQTT Publish
|
||||
void MyESP::mqttPublish(const char * topic, const char * payload) {
|
||||
// myDebug_P(PSTR("[MQTT] Sending pubish to %s with payload %s"), _mqttTopic(topic), payload); // for debugging
|
||||
mqttClient.publish(_mqttTopic(topic), _mqtt_qos, _mqtt_retain, payload);
|
||||
// Publish using the user's custom retain flag
|
||||
bool MyESP::mqttPublish(const char * topic, const char * payload) {
|
||||
// use the custom MQTT retain flag
|
||||
return mqttPublish(topic, payload, _mqtt_retain);
|
||||
}
|
||||
|
||||
_addMQTTLog(topic, payload, 1); // add to the log, using type of 1 for Publish
|
||||
// MQTT Publish
|
||||
// returns true if all good
|
||||
bool MyESP::mqttPublish(const char * topic, const char * payload, bool retain) {
|
||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||
//myDebug_P(PSTR("[MQTT] Sending publish to %s with payload %s"), _mqttTopic(topic), payload); // for debugging
|
||||
uint16_t packet_id = mqttClient.publish(_mqttTopic(topic), _mqtt_qos, retain, payload);
|
||||
|
||||
if (packet_id) {
|
||||
_addMQTTLog(topic, payload, 1); // add to the log, using type of 1 for Publish
|
||||
return true;
|
||||
} else {
|
||||
myDebug_P(PSTR("[MQTT] Error publishing to %s with payload %s [error %d]"), _mqttTopic(topic), payload, packet_id);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // failed
|
||||
}
|
||||
|
||||
// MQTT onConnect - when a connect is established
|
||||
void MyESP::_mqttOnConnect() {
|
||||
myDebug_P(PSTR("[MQTT] is connected"));
|
||||
myDebug_P(PSTR("[MQTT] MQTT connected established"));
|
||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
||||
|
||||
_mqtt_last_connection = millis();
|
||||
|
||||
// say we're alive to the Last Will topic
|
||||
mqttClient.publish(_mqttTopic(_mqtt_will_topic), 1, true, _mqtt_will_online_payload); // qos=1, retain=true
|
||||
mqttPublish(_mqtt_will_topic, _mqtt_will_online_payload, true); // force retain on
|
||||
|
||||
// subscribe to general subs
|
||||
mqttSubscribe(MQTT_TOPIC_RESTART);
|
||||
|
||||
// subscribe to a start message and send the first publish
|
||||
// forcing retain to off since we only want to send this once
|
||||
mqttSubscribe(MQTT_TOPIC_START);
|
||||
mqttPublish(MQTT_TOPIC_START, MQTT_TOPIC_START_PAYLOAD);
|
||||
mqttPublish(MQTT_TOPIC_START, MQTT_TOPIC_START_PAYLOAD, false);
|
||||
|
||||
// send heartbeat if enabled
|
||||
_heartbeatCheck(true);
|
||||
@@ -574,17 +601,8 @@ void MyESP::setTelnet(telnetcommand_callback_f callback_cmd, telnet_callback_f c
|
||||
}
|
||||
|
||||
void MyESP::_telnetConnected() {
|
||||
myDebug_P(PSTR("[TELNET] Telnet connection established"));
|
||||
_consoleShowHelp(); // Show the initial message
|
||||
|
||||
#ifdef CRASH
|
||||
// show crash dump if just restarted after a fatal crash
|
||||
uint32_t crash_time;
|
||||
EEPROMr.get(SAVE_CRASH_EEPROM_OFFSET + SAVE_CRASH_CRASH_TIME, crash_time);
|
||||
if ((crash_time != 0) && (crash_time != 0xFFFFFFFF)) {
|
||||
myDebug_P(PSTR("[SYSTEM] There is stack data available from the last system crash. Use 'crash dump' to view and 'crash clear' to reset"));
|
||||
}
|
||||
#endif
|
||||
myDebug_P(PSTR("[TELNET] Connected to %s version %s. Type ? for commands."), _app_name, _app_version);
|
||||
//_consoleShowHelp(); // Show the initial message
|
||||
|
||||
// call callback
|
||||
if (_telnet_callback_f) {
|
||||
@@ -593,7 +611,7 @@ void MyESP::_telnetConnected() {
|
||||
}
|
||||
|
||||
void MyESP::_telnetDisconnected() {
|
||||
myDebug_P(PSTR("[TELNET] Telnet connection closed"));
|
||||
// myDebug_P(PSTR("[TELNET] Telnet connection closed"));
|
||||
if (_telnet_callback_f) {
|
||||
(_telnet_callback_f)(TELNET_EVENT_DISCONNECT); // call callback
|
||||
}
|
||||
@@ -615,7 +633,7 @@ void MyESP::_telnet_setup() {
|
||||
// Show help of commands
|
||||
void MyESP::_consoleShowHelp() {
|
||||
myDebug_P(PSTR(""));
|
||||
myDebug_P(PSTR("* Connected to: %s version %s"), _app_name, _app_version);
|
||||
myDebug_P(PSTR("* %s version %s"), _app_name, _app_version);
|
||||
|
||||
if (isAPmode()) {
|
||||
myDebug_P(PSTR("* Device is in AP mode with SSID %s"), jw.getAPSSID().c_str());
|
||||
@@ -633,8 +651,15 @@ void MyESP::_consoleShowHelp() {
|
||||
myDebug_P(PSTR("* Commands:"));
|
||||
myDebug_P(PSTR("* ?/help=show commands, CTRL-D/quit=close telnet session"));
|
||||
myDebug_P(PSTR("* set, system, restart, mqttlog"));
|
||||
|
||||
#ifdef CRASH
|
||||
myDebug_P(PSTR("* crash <dump | clear | test [n]>"));
|
||||
// show crash dump if just restarted after a fatal crash
|
||||
uint32_t crash_time;
|
||||
EEPROMr.get(SAVE_CRASH_EEPROM_OFFSET + SAVE_CRASH_CRASH_TIME, crash_time);
|
||||
if ((crash_time) && (crash_time != 0xFFFFFFFF)) {
|
||||
myDebug_P(PSTR("[SYSTEM] There is stack data available from the last system crash. Use 'crash dump' to view and 'crash clear' to reset"));
|
||||
}
|
||||
#endif
|
||||
|
||||
// call callback function
|
||||
@@ -647,7 +672,7 @@ void MyESP::_consoleShowHelp() {
|
||||
|
||||
// see if a char * string is empty. It could not be initialized yet.
|
||||
// return true if there is a value
|
||||
bool MyESP::_hasValue(char * s) {
|
||||
bool MyESP::_hasValue(const char * s) {
|
||||
if ((s == nullptr) || (strlen(s) == 0)) {
|
||||
return false;
|
||||
}
|
||||
@@ -663,8 +688,11 @@ void MyESP::_printSetCommands() {
|
||||
myDebug_P(PSTR(" set mqtt_enabled <on | off>"));
|
||||
myDebug_P(PSTR(" set <mqtt_ip | mqtt_username | mqtt_password> [value]"));
|
||||
myDebug_P(PSTR(" set mqtt_heartbeat <on | off>"));
|
||||
myDebug_P(PSTR(" set mqtt_base [value]"));
|
||||
myDebug_P(PSTR(" set mqtt_port [value]"));
|
||||
myDebug_P(PSTR(" set mqtt_base [string]"));
|
||||
myDebug_P(PSTR(" set mqtt_port [number]"));
|
||||
myDebug_P(PSTR(" set mqtt_qos [0,1,2,3]"));
|
||||
myDebug_P(PSTR(" set mqtt_keepalive [seconds]"));
|
||||
myDebug_P(PSTR(" set mqtt_retain [on | off]"));
|
||||
myDebug_P(PSTR(" set ntp_enabled <on | off>"));
|
||||
myDebug_P(PSTR(" set serial <on | off>"));
|
||||
myDebug_P(PSTR(" set log_events <on | off>"));
|
||||
@@ -718,6 +746,9 @@ void MyESP::_printSetCommands() {
|
||||
myDebug_P(PSTR(" mqtt_base="));
|
||||
}
|
||||
myDebug_P(PSTR(" mqtt_port=%d"), _mqtt_port);
|
||||
myDebug_P(PSTR(" mqtt_keepalive=%d"), _mqtt_keepalive);
|
||||
myDebug_P(PSTR(" mqtt_retain=%d"), (_mqtt_retain) ? "on" : "off");
|
||||
myDebug_P(PSTR(" mqtt_qos=%d"), _mqtt_qos);
|
||||
myDebug_P(PSTR(" mqtt_heartbeat=%s"), (_mqtt_heartbeat) ? "on" : "off");
|
||||
|
||||
#ifdef FORCE_SERIAL
|
||||
@@ -765,148 +796,59 @@ char * MyESP::_telnet_readWord(bool allow_all_chars) {
|
||||
bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value) {
|
||||
bool save_config = false;
|
||||
bool save_custom_config = false;
|
||||
bool restart = false;
|
||||
|
||||
// check for our internal commands first
|
||||
if (strcmp(setting, "erase") == 0) {
|
||||
_fs_eraseConfig();
|
||||
return true;
|
||||
|
||||
} else if (strcmp(setting, "wifi_ssid") == 0) {
|
||||
if (value) {
|
||||
free(_network_ssid);
|
||||
_network_ssid = strdup(value);
|
||||
}
|
||||
save_config = true;
|
||||
save_config = fs_setSettingValue(&_network_ssid, value, "");
|
||||
restart = save_config;
|
||||
//jw.enableSTA(false);
|
||||
myDebug_P(PSTR("Note: please 'restart' to apply new WiFi settings"));
|
||||
} else if (strcmp(setting, "wifi_password") == 0) {
|
||||
if (value) {
|
||||
free(_network_password);
|
||||
_network_password = strdup(value);
|
||||
}
|
||||
save_config = true;
|
||||
save_config = fs_setSettingValue(&_network_password, value, "");
|
||||
restart = save_config;
|
||||
//jw.enableSTA(false);
|
||||
myDebug_P(PSTR("Note: please 'restart' to apply new WiFi settings"));
|
||||
|
||||
} else if (strcmp(setting, "wifi_mode") == 0) {
|
||||
if (value) {
|
||||
if (strcmp(value, "ap") == 0) {
|
||||
_network_wmode = 1;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Note: please 'restart' to apply new WiFi settings"));
|
||||
save_config = restart = true;
|
||||
} else if (strcmp(value, "client") == 0) {
|
||||
_network_wmode = 0;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Note: please 'restart' to apply new WiFi settings"));
|
||||
save_config = restart = true;
|
||||
} else {
|
||||
save_config = false;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (strcmp(setting, "mqtt_ip") == 0) {
|
||||
if (value) {
|
||||
free(_mqtt_ip);
|
||||
_mqtt_ip = strdup(value);
|
||||
}
|
||||
save_config = true;
|
||||
save_config = fs_setSettingValue(&_mqtt_ip, value, "");
|
||||
} else if (strcmp(setting, "mqtt_username") == 0) {
|
||||
if (value) {
|
||||
free(_mqtt_user);
|
||||
_mqtt_user = strdup(value);
|
||||
}
|
||||
save_config = true;
|
||||
save_config = fs_setSettingValue(&_mqtt_user, value, "");
|
||||
} else if (strcmp(setting, "mqtt_password") == 0) {
|
||||
if (value) {
|
||||
free(_mqtt_password);
|
||||
_mqtt_password = strdup(value);
|
||||
}
|
||||
save_config = true;
|
||||
save_config = fs_setSettingValue(&_mqtt_password, value, "");
|
||||
} else if (strcmp(setting, "mqtt_base") == 0) {
|
||||
if (value) {
|
||||
free(_mqtt_base);
|
||||
_mqtt_base = strdup(value);
|
||||
}
|
||||
save_config = true;
|
||||
save_config = fs_setSettingValue(&_mqtt_base, value, MQTT_BASE_DEFAULT);
|
||||
} else if (strcmp(setting, "mqtt_port") == 0) {
|
||||
if (value) {
|
||||
_mqtt_port = atoi(value);
|
||||
}
|
||||
save_config = true;
|
||||
save_config = fs_setSettingValue(&_mqtt_port, value, MQTT_PORT);
|
||||
} else if (strcmp(setting, "mqtt_keepalive") == 0) {
|
||||
save_config = fs_setSettingValue(&_mqtt_keepalive, value, MQTT_KEEPALIVE);
|
||||
} else if (strcmp(setting, "mqtt_qos") == 0) {
|
||||
save_config = fs_setSettingValue(&_mqtt_qos, value, MQTT_QOS);
|
||||
} else if (strcmp(setting, "mqtt_enabled") == 0) {
|
||||
save_config = true;
|
||||
if (value) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
_mqtt_enabled = true;
|
||||
save_config = true;
|
||||
} else if (strcmp(value, "off") == 0) {
|
||||
_mqtt_enabled = false;
|
||||
save_config = true;
|
||||
} else {
|
||||
save_config = false;
|
||||
}
|
||||
}
|
||||
|
||||
save_config = fs_setSettingValue(&_mqtt_enabled, value, false);
|
||||
} else if (strcmp(setting, "mqtt_retain") == 0) {
|
||||
save_config = fs_setSettingValue(&_mqtt_retain, value, MQTT_RETAIN);
|
||||
} else if (strcmp(setting, "serial") == 0) {
|
||||
save_config = true;
|
||||
if (value) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
_general_serial = true;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Type 'restart' to activate Serial mode."));
|
||||
} else if (strcmp(value, "off") == 0) {
|
||||
_general_serial = false;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Type 'restart' to deactivate Serial mode."));
|
||||
} else {
|
||||
save_config = false;
|
||||
}
|
||||
}
|
||||
|
||||
save_config = fs_setSettingValue(&_general_serial, value, false);
|
||||
restart = save_config;
|
||||
} else if (strcmp(setting, "mqtt_heartbeat") == 0) {
|
||||
save_config = true;
|
||||
if (value) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
_mqtt_heartbeat = true;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Heartbeat on"));
|
||||
} else if (strcmp(value, "off") == 0) {
|
||||
_mqtt_heartbeat = false;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Heartbeat off"));
|
||||
} else {
|
||||
save_config = false;
|
||||
}
|
||||
}
|
||||
save_config = fs_setSettingValue(&_mqtt_heartbeat, value, false);
|
||||
} else if (strcmp(setting, "ntp_enabled") == 0) {
|
||||
save_config = true;
|
||||
if (value) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
_ntp_enabled = true;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("NTP on"));
|
||||
} else if (strcmp(value, "off") == 0) {
|
||||
_ntp_enabled = false;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("NTP off"));
|
||||
} else {
|
||||
save_config = false;
|
||||
}
|
||||
}
|
||||
save_config = fs_setSettingValue(&_ntp_enabled, value, false);
|
||||
} else if (strcmp(setting, "log_events") == 0) {
|
||||
save_config = true;
|
||||
if (value) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
_general_log_events = true;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Event logging on"));
|
||||
} else if (strcmp(value, "off") == 0) {
|
||||
_general_log_events = false;
|
||||
save_config = true;
|
||||
myDebug_P(PSTR("Event logging off"));
|
||||
} else {
|
||||
save_config = false;
|
||||
}
|
||||
}
|
||||
save_config = fs_setSettingValue(&_general_log_events, value, false);
|
||||
} else {
|
||||
// finally check for any custom commands
|
||||
if (_fs_setlist_callback_f) {
|
||||
@@ -920,7 +862,7 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
||||
if ((save_config || save_custom_config)) {
|
||||
// check for 2 params
|
||||
if (value == nullptr) {
|
||||
myDebug_P(PSTR("%s setting reset to its default value."), setting);
|
||||
myDebug_P(PSTR("%s has been reset to its default value."), setting);
|
||||
} else {
|
||||
// must be 3 params
|
||||
myDebug_P(PSTR("%s changed."), setting);
|
||||
@@ -937,6 +879,10 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
||||
ok = _fs_createCustomConfig();
|
||||
}
|
||||
|
||||
if (restart) {
|
||||
myDebug_P(PSTR("Please 'restart' to apply new settings."));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -999,7 +945,7 @@ void MyESP::_telnetCommand(char * commandLine) {
|
||||
}
|
||||
|
||||
// restart command
|
||||
if ((strcmp(ptrToCommandName, "restart") == 0) && (wc == 1)) {
|
||||
if (((strcmp(ptrToCommandName, "restart") == 0) || (strcmp(ptrToCommandName, "reboot") == 0)) && (wc == 1)) {
|
||||
resetESP();
|
||||
return;
|
||||
}
|
||||
@@ -1315,7 +1261,7 @@ void MyESP::showSystemStats() {
|
||||
}
|
||||
|
||||
if (_ntp_enabled) {
|
||||
myDebug_P(PSTR(" [NTP] Time in UTC is %02d:%02d:%02d"), hour(now()), minute(now()), second(now()));
|
||||
myDebug_P(PSTR(" [NTP] Time in UTC is %02d:%02d:%02d"), to_hour(now()), to_minute(now()), to_second(now()));
|
||||
}
|
||||
|
||||
#ifdef CRASH
|
||||
@@ -1426,7 +1372,7 @@ void MyESP::_heartbeatCheck(bool force = false) {
|
||||
strlcat(payload, itoa(mem_available, s, 10), sizeof(payload)); // free mem as a %
|
||||
strlcat(payload, "%", sizeof(payload));
|
||||
|
||||
mqttPublish(MQTT_TOPIC_HEARTBEAT, payload); // send to MQTT
|
||||
mqttPublish(MQTT_TOPIC_HEARTBEAT, payload, false); // send to MQTT with retain off
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1570,27 +1516,27 @@ char * MyESP::_mqttTopic(const char * topic) {
|
||||
}
|
||||
|
||||
// validates a file in SPIFFS, loads it into the json buffer and returns true if ok
|
||||
bool MyESP::_fs_validateConfigFile(const char * filename, size_t maxsize, JsonDocument & doc) {
|
||||
size_t MyESP::_fs_validateConfigFile(const char * filename, size_t maxsize, JsonDocument & doc) {
|
||||
// see if we can open it
|
||||
File file = SPIFFS.open(filename, "r");
|
||||
if (!file) {
|
||||
myDebug_P(PSTR("[FS] File %s not found"), filename);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check size
|
||||
size_t size = file.size();
|
||||
|
||||
myDebug_P(PSTR("[FS] Checking file %s (size %d bytes)"), filename, size); // remove for debugging
|
||||
// myDebug_P(PSTR("[FS] Checking file %s (%d bytes)"), filename, size); // remove for debugging
|
||||
|
||||
if (size > maxsize) {
|
||||
file.close();
|
||||
myDebug_P(PSTR("[FS] File %s size %d is too large (max %d)"), filename, size, maxsize);
|
||||
return false;
|
||||
myDebug_P(PSTR("[FS] Error. File %s size %d is too large (max %d)"), filename, size, maxsize);
|
||||
return 0;
|
||||
} else if (size == 0) {
|
||||
file.close();
|
||||
myDebug_P(PSTR("[FS] Corrupted file %s"), filename);
|
||||
return false;
|
||||
myDebug_P(PSTR("[FS] Error. Corrupted file %s"), filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check integrity by reading file from SPIFFS into the char array
|
||||
@@ -1599,53 +1545,53 @@ bool MyESP::_fs_validateConfigFile(const char * filename, size_t maxsize, JsonDo
|
||||
size_t real_size = file.readBytes(buffer, size);
|
||||
if (real_size != size) {
|
||||
file.close();
|
||||
myDebug_P(PSTR("[FS] Error, file %s sizes don't match (%d/%d), looks corrupted"), filename, real_size, size);
|
||||
myDebug_P(PSTR("[FS] Error. File %s sizes don't match (%d/%d), looks corrupted"), filename, real_size, size);
|
||||
delete[] buffer;
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// now read into the given json
|
||||
DeserializationError error = deserializeJson(doc, buffer);
|
||||
if (error) {
|
||||
myDebug_P(PSTR("[FS] Failed to deserialize json, error %s"), error.c_str());
|
||||
myDebug_P(PSTR("[FS] Error. Failed to deserialize json, error %s"), error.c_str());
|
||||
delete[] buffer;
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// serializeJsonPretty(doc, Serial); // enable for debugging
|
||||
|
||||
file.close();
|
||||
delete[] buffer;
|
||||
return true;
|
||||
return size;
|
||||
}
|
||||
|
||||
// validates the event log file in SPIFFS
|
||||
// returns true if all OK
|
||||
bool MyESP::_fs_validateLogFile(const char * filename) {
|
||||
size_t MyESP::_fs_validateLogFile(const char * filename) {
|
||||
// exit if we have disabled logging
|
||||
if (!_general_log_events) {
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// see if we can open it
|
||||
File eventlog = SPIFFS.open(filename, "r");
|
||||
if (!eventlog) {
|
||||
myDebug_P(PSTR("[FS] File %s not found"), filename);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check sizes
|
||||
size_t size = eventlog.size();
|
||||
size_t maxsize = ESP.getFreeHeap() - 2000; // reserve some buffer
|
||||
myDebug_P(PSTR("[FS] Checking file %s (size %d bytes, max is %d)"), filename, size, maxsize); // remove for debugging
|
||||
size_t maxsize = ESP.getFreeHeap() - 2000; // reserve some buffer
|
||||
// myDebug_P(PSTR("[FS] Checking file %s (%d/%d bytes)"), filename, size, maxsize); // remove for debugging
|
||||
if (size > maxsize) {
|
||||
eventlog.close();
|
||||
myDebug_P(PSTR("[FS] File %s size %d is too large (max %d)"), filename, size, maxsize);
|
||||
return false;
|
||||
myDebug_P(PSTR("[FS] File %s size %d is too large"), filename, size);
|
||||
return 0;
|
||||
} else if (size == 0) {
|
||||
eventlog.close();
|
||||
myDebug_P(PSTR("[FS] Corrupted file %s"), filename);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1677,12 +1623,11 @@ bool MyESP::_fs_validateLogFile(const char * filename) {
|
||||
uint16_t char_count = 0;
|
||||
bool abort = false;
|
||||
char char_buffer[MYESP_JSON_LOG_MAXSIZE];
|
||||
char c;
|
||||
StaticJsonDocument<MYESP_JSON_LOG_MAXSIZE> doc;
|
||||
|
||||
// eventlog.seek(0);
|
||||
while (eventlog.available() && !abort) {
|
||||
c = eventlog.read(); // read a char
|
||||
char c = eventlog.read(); // read a char
|
||||
|
||||
// see if we have reached the end of the string
|
||||
if (c == '\0' || c == '\n') {
|
||||
@@ -1706,7 +1651,12 @@ bool MyESP::_fs_validateLogFile(const char * filename) {
|
||||
}
|
||||
|
||||
eventlog.close();
|
||||
return !abort;
|
||||
|
||||
if (abort) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
// format File System
|
||||
@@ -1738,8 +1688,9 @@ bool MyESP::_fs_loadConfig() {
|
||||
StaticJsonDocument<MYESP_SPIFFS_MAXSIZE_CONFIG> doc;
|
||||
|
||||
// set to true to print out contents of file
|
||||
if (!_fs_validateConfigFile(MYESP_CONFIG_FILE, MYESP_SPIFFS_MAXSIZE_CONFIG, doc)) {
|
||||
myDebug_P(PSTR("[FS] Failed to open system config"));
|
||||
size_t size = _fs_validateConfigFile(MYESP_CONFIG_FILE, MYESP_SPIFFS_MAXSIZE_CONFIG, doc);
|
||||
if (!size) {
|
||||
myDebug_P(PSTR("[FS] Error. Failed to open system config"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1752,7 +1703,7 @@ bool MyESP::_fs_loadConfig() {
|
||||
_general_password = strdup(general["password"] | MYESP_HTTP_PASSWORD);
|
||||
_ws->setAuthentication("admin", _general_password);
|
||||
_general_hostname = strdup(general["hostname"]);
|
||||
_general_log_events = general["log_events"] | false; // default is off
|
||||
_general_log_events = general["log_events"];
|
||||
|
||||
// serial is only on when booting
|
||||
#ifdef FORCE_SERIAL
|
||||
@@ -1768,6 +1719,9 @@ bool MyESP::_fs_loadConfig() {
|
||||
_mqtt_ip = strdup(mqtt["ip"] | "");
|
||||
_mqtt_user = strdup(mqtt["user"] | "");
|
||||
_mqtt_port = mqtt["port"] | MQTT_PORT;
|
||||
_mqtt_keepalive = mqtt["keepalive"] | MQTT_KEEPALIVE;
|
||||
_mqtt_retain = mqtt["retain"];
|
||||
_mqtt_qos = mqtt["qos"] | MQTT_QOS;
|
||||
_mqtt_password = strdup(mqtt["password"] | "");
|
||||
_mqtt_base = strdup(mqtt["base"] | MQTT_BASE_DEFAULT);
|
||||
|
||||
@@ -1778,7 +1732,65 @@ bool MyESP::_fs_loadConfig() {
|
||||
_ntp_interval = 60;
|
||||
_ntp_enabled = ntp["enabled"];
|
||||
|
||||
myDebug_P(PSTR("[FS] System config loaded"));
|
||||
myDebug_P(PSTR("[FS] System config loaded (%d bytes)"), size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// saves a string into a config setting, using default value if non set
|
||||
// returns true if successful
|
||||
bool MyESP::fs_setSettingValue(char ** setting, const char * value, const char * value_default) {
|
||||
if (*setting == nullptr) {
|
||||
free(*setting); // first free any allocated memory
|
||||
}
|
||||
|
||||
if (_hasValue(value)) {
|
||||
*setting = strdup(value);
|
||||
} else {
|
||||
*setting = strdup(value_default); // use the default value
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// saves a 2-byte short integer into a config setting, using default value if non set
|
||||
// returns true if successful
|
||||
bool MyESP::fs_setSettingValue(uint16_t * setting, const char * value, uint16_t value_default) {
|
||||
if (_hasValue(value)) {
|
||||
*setting = (uint16_t)atoi(value);
|
||||
} else {
|
||||
*setting = value_default; // use the default value
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// saves an 8-bit integer into a config setting, using default value if non set
|
||||
// returns true if successful
|
||||
bool MyESP::fs_setSettingValue(uint8_t * setting, const char * value, uint8_t value_default) {
|
||||
if (_hasValue(value)) {
|
||||
*setting = (uint8_t)atoi(value);
|
||||
} else {
|
||||
*setting = value_default; // use the default value
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// saves a bool into a config setting, using default value if non set
|
||||
// returns true if successful
|
||||
bool MyESP::fs_setSettingValue(bool * setting, const char * value, bool value_default) {
|
||||
if (_hasValue(value)) {
|
||||
if ((strcmp(value, "on") == 0) || (strcmp(value, "yes") == 0) || (strcmp(value, "1") == 0) || (strcmp(value, "true") == 0)) {
|
||||
*setting = true;
|
||||
} else if ((strcmp(value, "off") == 0) || (strcmp(value, "no") == 0) || (strcmp(value, "0") == 0) || (strcmp(value, "false") == 0)) {
|
||||
*setting = false;
|
||||
} else {
|
||||
return false; // invalid setting value
|
||||
}
|
||||
} else {
|
||||
*setting = value_default; // use the default value
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1787,8 +1799,9 @@ bool MyESP::_fs_loadConfig() {
|
||||
bool MyESP::_fs_loadCustomConfig() {
|
||||
StaticJsonDocument<MYESP_SPIFFS_MAXSIZE_CONFIG> doc;
|
||||
|
||||
if (!_fs_validateConfigFile(MYESP_CUSTOMCONFIG_FILE, MYESP_SPIFFS_MAXSIZE_CONFIG, doc)) {
|
||||
myDebug_P(PSTR("[FS] Failed to open custom config"));
|
||||
size_t size = _fs_validateConfigFile(MYESP_CUSTOMCONFIG_FILE, MYESP_SPIFFS_MAXSIZE_CONFIG, doc);
|
||||
if (!size) {
|
||||
myDebug_P(PSTR("[FS] Error. Failed to open custom config"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1798,7 +1811,7 @@ bool MyESP::_fs_loadCustomConfig() {
|
||||
myDebug_P(PSTR("[FS] Error reading custom config"));
|
||||
return false;
|
||||
} else {
|
||||
myDebug_P(PSTR("[FS] Custom config loaded"));
|
||||
myDebug_P(PSTR("[FS] Custom config loaded (%d bytes)"), size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1838,7 +1851,7 @@ bool MyESP::fs_saveCustomConfig(JsonObject root) {
|
||||
_writeEvent("INFO", "system", "Custom config stored in the SPIFFS", "");
|
||||
}
|
||||
|
||||
myDebug_P(PSTR("[FS] custom config saved"));
|
||||
// myDebug_P(PSTR("[FS] custom config saved"));
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
@@ -1873,7 +1886,7 @@ bool MyESP::fs_saveConfig(JsonObject root) {
|
||||
if (_general_log_events) {
|
||||
_writeEvent("INFO", "system", "System config stored in the SPIFFS", "");
|
||||
}
|
||||
myDebug_P(PSTR("[FS] system config saved"));
|
||||
// myDebug_P(PSTR("[FS] system config saved"));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -1899,10 +1912,11 @@ bool MyESP::_fs_writeConfig() {
|
||||
network["password"] = _network_password;
|
||||
network["wmode"] = _network_wmode;
|
||||
|
||||
JsonObject general = doc.createNestedObject("general");
|
||||
general["password"] = _general_password;
|
||||
general["serial"] = _general_serial;
|
||||
general["hostname"] = _general_hostname;
|
||||
JsonObject general = doc.createNestedObject("general");
|
||||
general["password"] = _general_password;
|
||||
general["serial"] = _general_serial;
|
||||
general["hostname"] = _general_hostname;
|
||||
general["log_events"] = _general_log_events;
|
||||
|
||||
JsonObject mqtt = doc.createNestedObject("mqtt");
|
||||
mqtt["enabled"] = _mqtt_enabled;
|
||||
@@ -1910,8 +1924,12 @@ bool MyESP::_fs_writeConfig() {
|
||||
mqtt["ip"] = _mqtt_ip;
|
||||
mqtt["user"] = _mqtt_user;
|
||||
mqtt["port"] = _mqtt_port;
|
||||
mqtt["password"] = _mqtt_password;
|
||||
mqtt["base"] = _mqtt_base;
|
||||
mqtt["qos"] = _mqtt_qos;
|
||||
mqtt["keepalive"] = _mqtt_keepalive;
|
||||
mqtt["retain"] = _mqtt_retain;
|
||||
|
||||
mqtt["password"] = _mqtt_password;
|
||||
mqtt["base"] = _mqtt_base;
|
||||
|
||||
JsonObject ntp = doc.createNestedObject("ntp");
|
||||
ntp["server"] = _ntp_server;
|
||||
@@ -1987,8 +2005,9 @@ void MyESP::_fs_setup() {
|
||||
*/
|
||||
|
||||
// validate the event log. Sometimes it can can corrupted.
|
||||
if (_fs_validateLogFile(MYESP_EVENTLOG_FILE)) {
|
||||
myDebug_P(PSTR("[FS] Event log is healthy"));
|
||||
size_t size = _fs_validateLogFile(MYESP_EVENTLOG_FILE);
|
||||
if (size) {
|
||||
myDebug_P(PSTR("[FS] Event log loaded (%d bytes)"), size);
|
||||
} else {
|
||||
myDebug_P(PSTR("[FS] Resetting event log"));
|
||||
SPIFFS.remove(MYESP_EVENTLOG_FILE);
|
||||
@@ -2284,12 +2303,11 @@ void MyESP::_sendEventLog(uint8_t page) {
|
||||
uint8_t line_count = 0;
|
||||
bool abort = false;
|
||||
char char_buffer[MYESP_JSON_LOG_MAXSIZE];
|
||||
char c;
|
||||
float pages;
|
||||
|
||||
// start at top and read until we find the page we want (sets of 10)
|
||||
while (eventlog.available() && !abort) {
|
||||
c = eventlog.read();
|
||||
char c = eventlog.read();
|
||||
|
||||
// see if we have reached the end of the string
|
||||
if (c == '\0' || c == '\n') {
|
||||
|
||||
61
src/MyESP.h
61
src/MyESP.h
@@ -9,7 +9,7 @@
|
||||
#ifndef MyESP_h
|
||||
#define MyESP_h
|
||||
|
||||
#define MYESP_VERSION "1.2.6"
|
||||
#define MYESP_VERSION "1.2.12"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ArduinoOTA.h>
|
||||
@@ -64,7 +64,7 @@ extern struct rst_info resetInfo;
|
||||
#define MYESP_LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms) = 30 seconds
|
||||
|
||||
// WIFI
|
||||
#define MYESP_WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms (10 seconds)
|
||||
#define MYESP_WIFI_CONNECT_TIMEOUT 20000 // Connecting timeout for WIFI in ms (20 seconds)
|
||||
#define MYESP_WIFI_RECONNECT_INTERVAL 600000 // If could not connect to WIFI, retry after this time in ms. 10 minutes
|
||||
|
||||
// MQTT
|
||||
@@ -79,22 +79,26 @@ extern struct rst_info resetInfo;
|
||||
#define MQTT_WILL_ONLINE_PAYLOAD "online" // for last will & testament payload
|
||||
#define MQTT_WILL_OFFLINE_PAYLOAD "offline" // for last will & testament payload
|
||||
#define MQTT_BASE_DEFAULT "home" // default MQTT prefix to topics
|
||||
#define MQTT_RETAIN false
|
||||
#define MQTT_KEEPALIVE 60 // 1 minute
|
||||
#define MQTT_QOS 1
|
||||
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
|
||||
#define MQTT_MAX_TOPIC_SIZE 50 // max length of MQTT topic
|
||||
#define MQTT_MAX_PAYLOAD_SIZE 500 // max size of a JSON object. See https://arduinojson.org/v6/assistant/
|
||||
#define MQTT_MAX_PAYLOAD_SIZE_LARGE 2000 // max size of a large JSON object, like for sending MQTT log
|
||||
#define MYESP_JSON_MAXSIZE 2000 // for large Dynamic json files
|
||||
#define MYESP_MQTTLOG_MAX 40 // max number of log entries for MQTT publishes and subscribes
|
||||
#define MYESP_JSON_LOG_MAXSIZE 300 // max size of an JSON log entry
|
||||
#define MQTT_RETAIN false // default false
|
||||
#define MQTT_KEEPALIVE 60 // default keepalive 1 minute
|
||||
#define MQTT_QOS 0 // default qos 0
|
||||
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
|
||||
#define MQTT_MAX_TOPIC_SIZE 50 // max length of MQTT topic
|
||||
#define MQTT_MAX_PAYLOAD_SIZE 700 // max size of a JSON object. See https://arduinojson.org/v6/assistant/
|
||||
#define MQTT_MAX_PAYLOAD_SIZE_LARGE 2000 // max size of a large JSON object, like for sending MQTT log
|
||||
|
||||
// Internal MQTT events
|
||||
#define MQTT_CONNECT_EVENT 0
|
||||
#define MQTT_DISCONNECT_EVENT 1
|
||||
#define MQTT_MESSAGE_EVENT 2
|
||||
|
||||
#define MYESP_JSON_MAXSIZE 2000 // for large Dynamic json files
|
||||
#define MYESP_MQTTLOG_MAX 60 // max number of log entries for MQTT publishes and subscribes
|
||||
#define MYESP_JSON_LOG_MAXSIZE 300 // max size of an JSON log entry
|
||||
|
||||
#define MYESP_MQTT_PAYLOAD_ON '1' // for MQTT switch on
|
||||
#define MYESP_MQTT_PAYLOAD_OFF '0' // for MQTT switch off
|
||||
|
||||
// Telnet
|
||||
#define TELNET_SERIAL_BAUD 115200
|
||||
#define TELNET_MAX_COMMAND_LENGTH 80 // length of a command
|
||||
@@ -268,9 +272,10 @@ class MyESP {
|
||||
|
||||
// mqtt
|
||||
bool isMQTTConnected();
|
||||
void mqttSubscribe(const char * topic);
|
||||
bool mqttSubscribe(const char * topic);
|
||||
void mqttUnsubscribe(const char * topic);
|
||||
void mqttPublish(const char * topic, const char * payload);
|
||||
bool mqttPublish(const char * topic, const char * payload);
|
||||
bool mqttPublish(const char * topic, const char * payload, bool retain);
|
||||
void setMQTT(mqtt_callback_f callback);
|
||||
|
||||
// OTA
|
||||
@@ -287,6 +292,10 @@ class MyESP {
|
||||
void setSettings(fs_loadsave_callback_f loadsave, fs_setlist_callback_f setlist, bool useSerial = true);
|
||||
bool fs_saveConfig(JsonObject root);
|
||||
bool fs_saveCustomConfig(JsonObject root);
|
||||
bool fs_setSettingValue(char ** setting, const char * value, const char * value_default);
|
||||
bool fs_setSettingValue(uint16_t * setting, const char * value, uint16_t value_default);
|
||||
bool fs_setSettingValue(uint8_t * setting, const char * value, uint8_t value_default);
|
||||
bool fs_setSettingValue(bool * setting, const char * value, bool value_default);
|
||||
|
||||
// Web
|
||||
void setWeb(web_callback_f callback_web);
|
||||
@@ -330,10 +339,10 @@ class MyESP {
|
||||
char * _mqtt_ip;
|
||||
char * _mqtt_user;
|
||||
char * _mqtt_password;
|
||||
int _mqtt_port;
|
||||
uint16_t _mqtt_port;
|
||||
char * _mqtt_base;
|
||||
bool _mqtt_enabled;
|
||||
uint32_t _mqtt_keepalive;
|
||||
uint16_t _mqtt_keepalive;
|
||||
uint8_t _mqtt_qos;
|
||||
bool _mqtt_retain;
|
||||
char * _mqtt_will_topic;
|
||||
@@ -377,15 +386,15 @@ class MyESP {
|
||||
bool _changeSetting(uint8_t wc, const char * setting, const char * value);
|
||||
|
||||
// fs and settings
|
||||
void _fs_setup();
|
||||
bool _fs_loadConfig();
|
||||
bool _fs_loadCustomConfig();
|
||||
void _fs_eraseConfig();
|
||||
bool _fs_writeConfig();
|
||||
bool _fs_createCustomConfig();
|
||||
bool _fs_sendConfig();
|
||||
bool _fs_validateConfigFile(const char * filename, size_t maxsize, JsonDocument & doc);
|
||||
bool _fs_validateLogFile(const char * filename);
|
||||
void _fs_setup();
|
||||
bool _fs_loadConfig();
|
||||
bool _fs_loadCustomConfig();
|
||||
void _fs_eraseConfig();
|
||||
bool _fs_writeConfig();
|
||||
bool _fs_createCustomConfig();
|
||||
bool _fs_sendConfig();
|
||||
size_t _fs_validateConfigFile(const char * filename, size_t maxsize, JsonDocument & doc);
|
||||
size_t _fs_validateLogFile(const char * filename);
|
||||
|
||||
fs_loadsave_callback_f _fs_loadsave_callback_f;
|
||||
fs_setlist_callback_f _fs_setlist_callback_f;
|
||||
@@ -406,7 +415,7 @@ class MyESP {
|
||||
bool _formatreq;
|
||||
unsigned long _getUptime();
|
||||
char * _getBuildTime();
|
||||
bool _hasValue(char * s);
|
||||
bool _hasValue(const char * s);
|
||||
void _printHeap(const char * s);
|
||||
|
||||
// reset reason and rtcmem
|
||||
|
||||
@@ -242,9 +242,8 @@ size_t TelnetSpy::write(uint8_t data) {
|
||||
sendBlock();
|
||||
}
|
||||
if (bufUsed == bufLen) {
|
||||
char c;
|
||||
while (bufUsed > 0) {
|
||||
c = pullTelnetBuf();
|
||||
char c = pullTelnetBuf();
|
||||
if (c == '\n') {
|
||||
addTelnetBuf('\r');
|
||||
break;
|
||||
@@ -573,7 +572,7 @@ void TelnetSpy::handle() {
|
||||
return;
|
||||
}
|
||||
if (!listening) {
|
||||
if ((WiFi.status() == WL_DISCONNECTED) && (WiFi.getMode() & WIFI_AP)) {
|
||||
if ((WiFi.status() == WL_DISCONNECTED) && (WiFi.getMode() & WIFI_AP)) {
|
||||
if (usedSer) {
|
||||
usedSer->println("[TELNET] in AP mode"); // added by Proddy
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ void breakTime(time_t timeInput, tmElements_t & tm) {
|
||||
days -= LEAP_YEAR(year) ? 366 : 365;
|
||||
time -= days; // now it is days in this year, starting at 0
|
||||
|
||||
days = 0;
|
||||
month = 0;
|
||||
monthLength = 0;
|
||||
for (month = 0; month < 12; month++) {
|
||||
@@ -104,36 +103,21 @@ void refreshCache(time_t t) {
|
||||
}
|
||||
}
|
||||
|
||||
int day(time_t t) { // the day for the given time (0-6)
|
||||
refreshCache(t);
|
||||
return tm.Day;
|
||||
}
|
||||
|
||||
int month(time_t t) { // the month for the given time
|
||||
refreshCache(t);
|
||||
return tm.Month;
|
||||
}
|
||||
|
||||
int second(time_t t) { // the second for the given time
|
||||
uint8_t to_second(time_t t) { // the second for the given time
|
||||
refreshCache(t);
|
||||
return tm.Second;
|
||||
}
|
||||
|
||||
int minute(time_t t) { // the minute for the given time
|
||||
uint8_t to_minute(time_t t) { // the minute for the given time
|
||||
refreshCache(t);
|
||||
return tm.Minute;
|
||||
}
|
||||
|
||||
int hour(time_t t) { // the hour for the given time
|
||||
uint8_t to_hour(time_t t) { // the hour for the given time
|
||||
refreshCache(t);
|
||||
return tm.Hour;
|
||||
}
|
||||
|
||||
int year(time_t t) { // the year for the given time
|
||||
refreshCache(t);
|
||||
return tmYearToCalendar(tm.Year);
|
||||
}
|
||||
|
||||
void setTime(time_t t) {
|
||||
sysTime = (uint32_t)t;
|
||||
nextSyncTime = (uint32_t)t + syncInterval;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _Time_h
|
||||
#define _Time_h
|
||||
#ifndef _TimeLib_h
|
||||
#define _TimeLib_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
@@ -27,7 +27,7 @@ typedef struct {
|
||||
uint8_t Day;
|
||||
uint8_t Month;
|
||||
uint8_t Year; // offset from 1970;
|
||||
} tmElements_t, TimeElements, *tmElementsPtr_t;
|
||||
} tmElements_t;
|
||||
|
||||
typedef time_t (*getExternalTime)();
|
||||
|
||||
@@ -38,12 +38,8 @@ void setSyncProvider(getExternalTime getTimeFunction); // identify the e
|
||||
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
|
||||
time_t makeTime(const tmElements_t & tm); // convert time elements into time_t
|
||||
|
||||
int hour(time_t t); // the hour for the given time
|
||||
int minute(time_t t); // the minute for the given time
|
||||
int second(time_t t); // the second for the given time
|
||||
int day(time_t t); // the day for the given time
|
||||
int month(time_t t); // the month for the given time
|
||||
int weekday(time_t t); // the weekday for the given time
|
||||
int year(time_t t); // the year for the given time
|
||||
uint8_t to_hour(time_t t); // the hour for the given time
|
||||
uint8_t to_minute(time_t t); // the minute for the given time
|
||||
uint8_t to_second(time_t t); // the second for the given time
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -97,8 +97,6 @@ void DS18::loop() {
|
||||
char * DS18::getDeviceString(char * buffer, unsigned char index) {
|
||||
uint8_t size = 128;
|
||||
if (index < _count) {
|
||||
uint8_t * address = _devices[index].address;
|
||||
|
||||
unsigned char chip_id = chip(index);
|
||||
if (chip_id == DS18_CHIP_DS18S20) {
|
||||
strlcpy(buffer, "DS18S20", size);
|
||||
@@ -112,6 +110,8 @@ char * DS18::getDeviceString(char * buffer, unsigned char index) {
|
||||
strlcpy(buffer, "Unknown", size);
|
||||
}
|
||||
|
||||
/*
|
||||
uint8_t * address = _devices[index].address;
|
||||
char a[30] = {0};
|
||||
snprintf(a,
|
||||
sizeof(a),
|
||||
@@ -127,6 +127,7 @@ char * DS18::getDeviceString(char * buffer, unsigned char index) {
|
||||
_gpio);
|
||||
|
||||
strlcat(buffer, a, size);
|
||||
*/
|
||||
} else {
|
||||
strlcpy(buffer, "invalid", size);
|
||||
}
|
||||
|
||||
884
src/ems-esp.cpp
884
src/ems-esp.cpp
File diff suppressed because it is too large
Load Diff
210
src/ems.cpp
210
src/ems.cpp
@@ -9,6 +9,7 @@
|
||||
#include "ems.h"
|
||||
#include "MyESP.h"
|
||||
#include "ems_devices.h"
|
||||
#include "ems_utils.h"
|
||||
#include "emsuart.h"
|
||||
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer
|
||||
|
||||
@@ -17,10 +18,6 @@
|
||||
uint8_t _TEST_DATA_max = ArraySize(TEST_DATA);
|
||||
#endif
|
||||
|
||||
// MyESP class for logging to telnet and serial
|
||||
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
|
||||
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
||||
|
||||
_EMS_Sys_Status EMS_Sys_Status; // EMS Status
|
||||
|
||||
CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO queue for Tx send buffer
|
||||
@@ -99,6 +96,9 @@ void _process_RCPLUSStatusMode(_EMS_RxTelegram * EMS_RxTelegram);
|
||||
// Junkers FR10 & FW100
|
||||
void _process_JunkersStatusMessage(_EMS_RxTelegram * EMS_RxTelegram);
|
||||
|
||||
// Mixers MM100
|
||||
void _process_MMPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram);
|
||||
|
||||
/**
|
||||
* Recognized EMS types and the functions they call to process the telegrams
|
||||
* Format: MODEL ID, TYPE ID, Description, function, emsplus
|
||||
@@ -184,8 +184,11 @@ const _EMS_Type EMS_Types[] = {
|
||||
{EMS_MODEL_ALL, EMS_TYPE_RCPLUSStatusMode, "RCPLUSStatusMode", _process_RCPLUSStatusMode},
|
||||
|
||||
// Junkers FR10
|
||||
{EMS_MODEL_ALL, EMS_TYPE_JunkersStatusMessage, "JunkersStatusMessage", _process_JunkersStatusMessage}
|
||||
{EMS_MODEL_ALL, EMS_TYPE_JunkersStatusMessage, "JunkersStatusMessage", _process_JunkersStatusMessage},
|
||||
|
||||
// Mixing devices
|
||||
{EMS_MODEL_MM100, EMS_TYPE_MMPLUSStatusMessage_HC1, "MMPLUSStatusMessage_HC1", _process_MMPLUSStatusMessage},
|
||||
{EMS_MODEL_MM100, EMS_TYPE_MMPLUSStatusMessage_HC2, "MMPLUSStatusMessage_HC2", _process_MMPLUSStatusMessage},
|
||||
|
||||
};
|
||||
|
||||
@@ -196,12 +199,14 @@ uint8_t _SolarModule_Devices_max = ArraySize(SolarModule_Devices); // number of
|
||||
uint8_t _Other_Devices_max = ArraySize(Other_Devices); // number of other ems devices
|
||||
uint8_t _Thermostat_Devices_max = ArraySize(Thermostat_Devices); // number of defined thermostat types
|
||||
uint8_t _HeatPump_Devices_max = ArraySize(HeatPump_Devices); // number of defined heatpump types
|
||||
uint8_t _Mixing_Devices_max = ArraySize(Mixing_Devices); // number of mixing device types
|
||||
|
||||
// these structs contain the data we store from the specific EMS devices
|
||||
_EMS_Boiler EMS_Boiler; // for boiler
|
||||
_EMS_Thermostat EMS_Thermostat; // for thermostat
|
||||
_EMS_SolarModule EMS_SolarModule; // for solar modules
|
||||
_EMS_HeatPump EMS_HeatPump; // for heatpumps
|
||||
_EMS_Mixing EMS_Mixing; // for mixing devices
|
||||
_EMS_Other EMS_Other; // for other known EMS devices
|
||||
|
||||
// CRC lookup table with poly 12 for faster checking
|
||||
@@ -223,7 +228,6 @@ const uint8_t TX_WRITE_TIMEOUT_COUNT = 2; // 3 retries before timeout
|
||||
const uint32_t EMS_BUS_TIMEOUT = 15000; // timeout in ms before recognizing the ems bus is offline (15 seconds)
|
||||
const uint32_t EMS_POLL_TIMEOUT = 5000000; // timeout in microseconds before recognizing the ems bus is offline (5 seconds)
|
||||
|
||||
|
||||
// init stats and counters and buffers
|
||||
void ems_init() {
|
||||
ems_clearDeviceList(); // init the device map
|
||||
@@ -267,6 +271,18 @@ void ems_init() {
|
||||
EMS_Thermostat.hc[i].curr_roomTemp = EMS_VALUE_SHORT_NOTSET;
|
||||
}
|
||||
|
||||
EMS_Mixing.detected = false;
|
||||
// init all mixing modules
|
||||
for (uint8_t i = 0; i < EMS_THERMOSTAT_MAXHC; i++) {
|
||||
EMS_Mixing.hc[i].hc = i + 1;
|
||||
EMS_Mixing.hc[i].flowTemp = EMS_VALUE_SHORT_NOTSET;
|
||||
EMS_Mixing.hc[i].pumpMod = EMS_VALUE_INT_NOTSET;
|
||||
EMS_Mixing.hc[i].valveStatus = EMS_VALUE_INT_NOTSET;
|
||||
EMS_Mixing.hc[i].device_id = EMS_ID_NONE;
|
||||
EMS_Mixing.hc[i].model_id = EMS_MODEL_NONE;
|
||||
EMS_Mixing.hc[i].product_id = EMS_ID_NONE;
|
||||
}
|
||||
|
||||
// UBAParameterWW
|
||||
EMS_Boiler.wWActivated = EMS_VALUE_INT_NOTSET; // Warm Water activated
|
||||
EMS_Boiler.wWSelTemp = EMS_VALUE_INT_NOTSET; // Warm Water selected temperature
|
||||
@@ -378,6 +394,10 @@ bool ems_getThermostatEnabled() {
|
||||
return (EMS_Thermostat.device_id != EMS_ID_NONE);
|
||||
}
|
||||
|
||||
bool ems_getMixingDeviceEnabled() {
|
||||
return EMS_Mixing.detected;
|
||||
}
|
||||
|
||||
bool ems_getSolarModuleEnabled() {
|
||||
return (EMS_SolarModule.device_id != EMS_ID_NONE);
|
||||
}
|
||||
@@ -424,9 +444,13 @@ _EMS_SYS_LOGGING ems_getLogging() {
|
||||
return EMS_Sys_Status.emsLogging;
|
||||
}
|
||||
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel, bool silent) {
|
||||
if (loglevel <= EMS_SYS_LOGGING_JABBER) {
|
||||
EMS_Sys_Status.emsLogging = loglevel;
|
||||
if (silent) {
|
||||
return; // don't print to telnet/serial
|
||||
}
|
||||
|
||||
if (loglevel == EMS_SYS_LOGGING_NONE) {
|
||||
myDebug_P(PSTR("System Logging set to None"));
|
||||
} else if (loglevel == EMS_SYS_LOGGING_BASIC) {
|
||||
@@ -472,36 +496,6 @@ uint8_t _crcCalculator(uint8_t * data, uint8_t len) {
|
||||
return crc;
|
||||
}
|
||||
|
||||
// like itoa but for hex, and quicker
|
||||
char * _hextoa(uint8_t value, char * buffer) {
|
||||
char * p = buffer;
|
||||
byte nib1 = (value >> 4) & 0x0F;
|
||||
byte nib2 = (value >> 0) & 0x0F;
|
||||
*p++ = nib1 < 0xA ? '0' + nib1 : 'A' + nib1 - 0xA;
|
||||
*p++ = nib2 < 0xA ? '0' + nib2 : 'A' + nib2 - 0xA;
|
||||
*p = '\0'; // null terminate just in case
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// for decimals 0 to 99, printed as a 2 char string
|
||||
char * _smallitoa(uint8_t value, char * buffer) {
|
||||
buffer[0] = ((value / 10) == 0) ? '0' : (value / 10) + '0';
|
||||
buffer[1] = (value % 10) + '0';
|
||||
buffer[2] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* for decimals 0 to 999, printed as a string
|
||||
* From @nomis
|
||||
*/
|
||||
char * _smallitoa3(uint16_t value, char * buffer) {
|
||||
buffer[0] = ((value / 100) == 0) ? '0' : (value / 100) + '0';
|
||||
buffer[1] = (((value % 100) / 10) == 0) ? '0' : ((value % 100) / 10) + '0';
|
||||
buffer[2] = (value % 10) + '0';
|
||||
buffer[3] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the pointer to the EMS_Types array for a given type ID
|
||||
* or -1 if not found
|
||||
@@ -606,10 +600,11 @@ void _ems_sendTelegram() {
|
||||
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length); // add the CRC
|
||||
|
||||
if (EMS_Sys_Status.emsLogging != EMS_SYS_LOGGING_NONE) {
|
||||
_EMS_RxTelegram EMS_RxTelegram; // create new Rx object
|
||||
EMS_RxTelegram.length = EMS_TxTelegram.length; // full length of telegram
|
||||
EMS_RxTelegram.telegram = EMS_TxTelegram.data;
|
||||
EMS_RxTelegram.timestamp = millis(); // now
|
||||
_EMS_RxTelegram EMS_RxTelegram; // create new Rx object
|
||||
EMS_RxTelegram.length = EMS_TxTelegram.length; // full length of telegram
|
||||
EMS_RxTelegram.telegram = EMS_TxTelegram.data;
|
||||
EMS_RxTelegram.data_length = 0; // ignore #data=
|
||||
EMS_RxTelegram.timestamp = millis(); // now
|
||||
_debugPrintTelegram("Sending raw: ", &EMS_RxTelegram, COLOR_CYAN, true);
|
||||
}
|
||||
|
||||
@@ -633,7 +628,7 @@ void _ems_sendTelegram() {
|
||||
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest;
|
||||
} else {
|
||||
// for a READ or VALIDATE
|
||||
EMS_TxTelegram.data[1] = (EMS_TxTelegram.dest | 0x80); // read has 8th bit set, always
|
||||
EMS_TxTelegram.data[1] = (EMS_TxTelegram.dest | 0x80); // read has 8th bit set, always
|
||||
}
|
||||
|
||||
// complete the rest of the header depending on EMS or EMS+
|
||||
@@ -933,7 +928,6 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
// Assume at this point we have something that vaguely resembles a telegram in the format [src] [dest] [type] [offset] [data] [crc]
|
||||
// validate the CRC, if it's bad ignore it
|
||||
if (telegram[length - 1] != _crcCalculator(telegram, length)) {
|
||||
LA_PULSE(200);
|
||||
EMS_Sys_Status.emxCrcErr++;
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||
_debugPrintTelegram("Corrupt telegram: ", &EMS_RxTelegram, COLOR_RED, true);
|
||||
@@ -1010,7 +1004,7 @@ void _printMessage(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s));
|
||||
}
|
||||
|
||||
if (length != 0) {
|
||||
if (length) {
|
||||
// type
|
||||
strlcat(output_str, ", type 0x", sizeof(output_str));
|
||||
|
||||
@@ -1135,7 +1129,7 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
// release the lock on the TxQueue
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
|
||||
|
||||
// at this point we can assume TxStatus is EMS_TX_STATUS_WAIT as we just sent a read or validate
|
||||
// at this point we can assume TxStatus was EMS_TX_STATUS_WAIT as we just sent a read or validate telegram
|
||||
// for READ or VALIDATE the dest (telegram[1]) is always us, so check for this
|
||||
// and if not we probably didn't get any response so remove the last Tx from the queue and process the telegram anyway
|
||||
if ((telegram[1] & 0x7F) != EMS_ID_ME) {
|
||||
@@ -1345,6 +1339,7 @@ void _process_UBAMonitorSlow(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
EMS_Boiler.burnStarts = _toLong(10);
|
||||
EMS_Boiler.burnWorkMin = _toLong(13);
|
||||
EMS_Boiler.heatWorkMin = _toLong(19);
|
||||
EMS_Boiler.switchTemp = _toShort(25);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1464,6 +1459,21 @@ void _process_EasyStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
|
||||
}
|
||||
|
||||
void _process_MMPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
uint8_t hc = (EMS_RxTelegram->type - EMS_TYPE_MMPLUSStatusMessage_HC1); // 0 to 3
|
||||
if (hc >= EMS_THERMOSTAT_MAXHC) {
|
||||
return; // invalid type
|
||||
}
|
||||
EMS_Mixing.hc[hc].active = true;
|
||||
|
||||
if (EMS_RxTelegram->data_length == 1) {
|
||||
} else if (EMS_RxTelegram->data_length > 8) {
|
||||
EMS_Mixing.hc[hc].flowTemp = _toShort(EMS_OFFSET_MMPLUSStatusMessage_flow_temp);
|
||||
EMS_Mixing.hc[hc].pumpMod = _toByte(EMS_OFFSET_MMPLUSStatusMessage_pump_mod);
|
||||
EMS_Mixing.hc[hc].valveStatus = _toByte(EMS_OFFSET_MMPLUSStatusMessage_valve_status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* type 0x01A5 - data from the Nefit RC1010/3000 thermostat (0x18) and RC300/310s on 0x10
|
||||
* EMS+ messages may come in with different offsets so handle them here
|
||||
@@ -1471,7 +1481,7 @@ void _process_EasyStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
void _process_RCPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
// figure out which heating circuit
|
||||
uint8_t hc = (EMS_RxTelegram->type - EMS_TYPE_RCPLUSStatusMessage_HC1); // 0 to 3
|
||||
if (hc > 4) {
|
||||
if (hc >= EMS_THERMOSTAT_MAXHC) {
|
||||
return; // invalid type
|
||||
}
|
||||
EMS_Thermostat.hc[hc].active = true;
|
||||
@@ -1520,14 +1530,17 @@ void _process_RCPLUSStatusMode(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
* FR10 Junkers - type x006F
|
||||
*/
|
||||
void _process_JunkersStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
if (EMS_RxTelegram->offset == 0) {
|
||||
if (EMS_RxTelegram->offset == 0 && EMS_RxTelegram->data_length > 1) {
|
||||
uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1
|
||||
EMS_Thermostat.hc[hc].active = true;
|
||||
|
||||
// e.g. for FR10: 90 00 FF 00 00 6F 03 01 00 BE 00 BF
|
||||
// e.g. for FW100: 90 00 FF 00 00 6F 03 02 00 D7 00 DA F3 34 00 C4
|
||||
|
||||
EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(EMS_OFFSET_JunkersStatusMessage_curr); // value is * 10
|
||||
EMS_Thermostat.hc[hc].setpoint_roomTemp = _toShort(EMS_OFFSET_JunkersStatusMessage_setpoint); // value is * 10
|
||||
EMS_Thermostat.hc[hc].mode = _toByte(EMS_OFFSET_JunkersStatusMessage_mode);
|
||||
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1681,7 +1694,7 @@ void _process_SM10Monitor(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
*/
|
||||
void _process_SM100Monitor(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
// only process the complete telegram, not partial
|
||||
if (EMS_RxTelegram->offset != 0) {
|
||||
if (EMS_RxTelegram->offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1792,7 +1805,7 @@ void _process_ISM1Set(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
*/
|
||||
void _process_SetPoints(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||
if (EMS_RxTelegram->data_length != 0) {
|
||||
if (EMS_RxTelegram->data_length) {
|
||||
uint8_t setpoint = EMS_RxTelegram->data[0]; // flow temp
|
||||
//uint8_t ww_power = data[2]; // power in %
|
||||
|
||||
@@ -1884,6 +1897,10 @@ void _addDevice(uint8_t model_type, uint8_t src, uint8_t product_id, char * vers
|
||||
strlcat(device_type, "Heat Pump", sizeof(device_type));
|
||||
strlcpy(device.model_string, HeatPump_Devices[i].model_string, sizeof(device.model_string));
|
||||
break;
|
||||
case EMS_MODELTYPE_MIXING:
|
||||
strlcat(device_type, "Mixing Device", sizeof(device_type));
|
||||
strlcpy(device.model_string, Mixing_Devices[i].model_string, sizeof(device.model_string));
|
||||
break;
|
||||
case EMS_MODELTYPE_OTHER:
|
||||
strlcat(device_type, "Other", sizeof(device_type));
|
||||
strlcpy(device.model_string, Other_Devices[i].model_string, sizeof(device.model_string));
|
||||
@@ -2028,12 +2045,6 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
EMS_Boiler.product_id = Boiler_Devices[i].product_id;
|
||||
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
|
||||
|
||||
// check to see if its a Junkers Heatronic 3, which has a different poll'ing logic
|
||||
if (EMS_Boiler.product_id == EMS_PRODUCTID_HEATRONIC) {
|
||||
EMS_Sys_Status.emsIDMask = 0x80;
|
||||
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME ^ EMS_Sys_Status.emsIDMask;
|
||||
}
|
||||
|
||||
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
|
||||
}
|
||||
return;
|
||||
@@ -2123,6 +2134,23 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
return;
|
||||
}
|
||||
|
||||
// look for mixing devices
|
||||
i = 0;
|
||||
while (i < _Mixing_Devices_max) {
|
||||
if (Mixing_Devices[i].product_id == product_id) {
|
||||
typeFound = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (typeFound) {
|
||||
_addDevice(EMS_MODELTYPE_MIXING, EMS_RxTelegram->src, product_id, version, i);
|
||||
ems_doReadCommand(EMS_TYPE_MMPLUSStatusMessage_HC1, EMS_RxTelegram->src);
|
||||
EMS_Mixing.detected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// finally look for the other EMS devices
|
||||
i = 0;
|
||||
while (i < _Other_Devices_max) {
|
||||
@@ -2142,47 +2170,12 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we have a Junkers Heatronic 3 compatible device
|
||||
* Do a read command for the version with the src having the MSB set
|
||||
*/
|
||||
void _ems_detectJunkers() {
|
||||
#ifdef JUNKERS_DETECT
|
||||
char s[30] = {0};
|
||||
snprintf(s, sizeof(s), "%02X %02X %02X 00 %02X", (EMS_ID_ME | 0x80), (EMS_ID_BOILER | 0x080), EMS_TYPE_Version, EMS_MAX_TELEGRAM_LENGTH);
|
||||
ems_sendRawTelegram(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out the boiler and thermostat types
|
||||
*/
|
||||
void ems_discoverModels() {
|
||||
myDebug_P(PSTR("Starting auto discover of EMS devices..."));
|
||||
//myDebug_P(PSTR("Starting auto discover of EMS devices..."));
|
||||
ems_doReadCommand(EMS_TYPE_UBADevices, EMS_ID_BOILER);
|
||||
|
||||
// ems_startupTelegrams();
|
||||
|
||||
// TODO remove this part eventually, ems_discoverModels()
|
||||
/*
|
||||
|
||||
// boiler...
|
||||
// ems_doReadCommand(EMS_TYPE_Version, EMS_ID_BOILER);
|
||||
// _ems_detectJunkers(); // special hack for Junkers detection
|
||||
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_ID_SM); // check if there is Solar Module available
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_ID_HP); // check if there is HeatPump Module available
|
||||
|
||||
// thermostat...
|
||||
// if it hasn't been set, auto discover it
|
||||
if (EMS_Thermostat.device_id == EMS_ID_NONE) {
|
||||
ems_scanDevices(); // auto-discover it
|
||||
} else {
|
||||
// set the model as hardcoded (see my_devices.h) and fetch the version and product id
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.device_id);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2531,9 +2524,6 @@ void ems_scanDevices() {
|
||||
for (uint8_t device_id : Device_Ids) {
|
||||
ems_doReadCommand(EMS_TYPE_Version, device_id);
|
||||
}
|
||||
|
||||
// add a check for Junkers onto the queue
|
||||
_ems_detectJunkers();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2784,7 +2774,7 @@ void ems_setThermostatTemp(float temperature, uint8_t hc_num, uint8_t temptype)
|
||||
return;
|
||||
}
|
||||
|
||||
if (hc_num < 1 || hc_num > 4) {
|
||||
if (hc_num < 1 || hc_num > EMS_THERMOSTAT_MAXHC) {
|
||||
myDebug_P(PSTR("Invalid HC number"));
|
||||
return;
|
||||
}
|
||||
@@ -2799,7 +2789,11 @@ void ems_setThermostatTemp(float temperature, uint8_t hc_num, uint8_t temptype)
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||
EMS_TxTelegram.dest = device_id;
|
||||
|
||||
myDebug_P(PSTR("Setting new thermostat temperature for heating circuit %d type %d (0=auto,1=night,2=day,3=holiday)"), hc_num, temptype);
|
||||
char s[10] = {0};
|
||||
myDebug_P(PSTR("Setting new thermostat temperature to %s for heating circuit %d type %d (0=auto,1=night,2=day,3=holiday)"),
|
||||
_float_to_char(s, temperature),
|
||||
hc_num,
|
||||
temptype);
|
||||
|
||||
if (model_id == EMS_MODEL_RC20) {
|
||||
EMS_TxTelegram.type = EMS_TYPE_RC20Set;
|
||||
@@ -2894,24 +2888,38 @@ void ems_setThermostatMode(uint8_t mode, uint8_t hc_num) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hc_num < 1 || hc_num > 4) {
|
||||
if (hc_num < 1 || hc_num > EMS_THERMOSTAT_MAXHC) {
|
||||
myDebug_P(PSTR("Invalid HC number"));
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t model_id = EMS_Thermostat.model_id;
|
||||
uint8_t device_id = EMS_Thermostat.device_id;
|
||||
uint8_t set_mode;
|
||||
|
||||
// RC300/1000/3000 have different settings
|
||||
if (model_id == EMS_MODEL_RC300) {
|
||||
if (mode == 1) {
|
||||
mode = 0; // manual
|
||||
set_mode = 0; // manual/heat
|
||||
} else {
|
||||
mode = 0xFF; // auto
|
||||
set_mode = 0xFF; // auto
|
||||
}
|
||||
} else {
|
||||
set_mode = mode;
|
||||
}
|
||||
|
||||
myDebug_P(PSTR("Setting thermostat mode to %d for heating circuit %d"), mode, hc_num);
|
||||
// 0=off, 1=manual, 2=auto, 3=night, 4=day
|
||||
if (mode == 0) {
|
||||
myDebug_P(PSTR("Setting thermostat mode to off for heating circuit %d"), hc_num);
|
||||
} else if (set_mode == 1) {
|
||||
myDebug_P(PSTR("Setting thermostat mode to manual for heating circuit %d"), hc_num);
|
||||
} else if (set_mode == 2) {
|
||||
myDebug_P(PSTR("Setting thermostat mode to auto for heating circuit %d"), hc_num);
|
||||
} else if (set_mode == 3) {
|
||||
myDebug_P(PSTR("Setting thermostat mode to night for heating circuit %d"), hc_num);
|
||||
} else if (set_mode == 4) {
|
||||
myDebug_P(PSTR("Setting thermostat mode to day for heating circuit %d"), hc_num);
|
||||
}
|
||||
|
||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||
@@ -2920,7 +2928,7 @@ void ems_setThermostatMode(uint8_t mode, uint8_t hc_num) {
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||
EMS_TxTelegram.dest = device_id;
|
||||
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
|
||||
EMS_TxTelegram.dataValue = mode;
|
||||
EMS_TxTelegram.dataValue = set_mode;
|
||||
|
||||
// handle different thermostat types
|
||||
if (model_id == EMS_MODEL_RC20) {
|
||||
|
||||
107
src/ems.h
107
src/ems.h
@@ -4,7 +4,6 @@
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See ChangeLog.md for history
|
||||
* See README.md for Acknowledgments
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -13,67 +12,8 @@
|
||||
#include <Arduino.h>
|
||||
#include <list> // std::list
|
||||
|
||||
/* debug helper for logic analyzer
|
||||
* create marker puls on GPIOx
|
||||
* ° for Rx, we use GPIO14
|
||||
* ° for Tx, we use GPIO12
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
#ifdef LOGICANALYZER
|
||||
#define RX_MARK_PIN 14
|
||||
#define TX_MARK_PIN 12
|
||||
|
||||
#define RX_MARK_MASK (1 << RX_MARK_PIN)
|
||||
#define TX_MARK_MASK (1 << TX_MARK_PIN)
|
||||
#define MARKERS_MASK (RX_MARK_PIN | TX_MARK_PIN)
|
||||
|
||||
#define GPIO_H(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (mask)))
|
||||
#define GPIO_L(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (mask)))
|
||||
|
||||
#define RX_PULSE(pulse) \
|
||||
do { \
|
||||
GPIO_H(RX_MARK_MASK); \
|
||||
delayMicroseconds(pulse); \
|
||||
GPIO_L(RX_MARK_MASK); \
|
||||
} while (0)
|
||||
#define TX_PULSE(pulse) \
|
||||
do { \
|
||||
GPIO_H(TX_MARK_MASK); \
|
||||
delayMicroseconds(pulse); \
|
||||
GPIO_L(TX_MARK_MASK); \
|
||||
} while (0)
|
||||
#define LA_PULSE(pulse) \
|
||||
do { \
|
||||
GPIO_H(MARKERS_MASK); \
|
||||
delayMicroseconds(pulse); \
|
||||
GPIO_L(MARKERS_MASK); \
|
||||
} while (0)
|
||||
|
||||
#define INIT_MARKERS(void) \
|
||||
do { \
|
||||
pinMode(RX_MARK_PIN, OUTPUT); \
|
||||
pinMode(TX_MARK_PIN, OUTPUT); \
|
||||
GPIO_L(MARKERS_MASK); \
|
||||
} while (0)
|
||||
#else
|
||||
#define RX_PULSE(pulse) \
|
||||
{}
|
||||
#define TX_PULSE(pulse) \
|
||||
{}
|
||||
#define LA_PULSE(pulse) \
|
||||
{}
|
||||
#define INIT_MARKERS(void) \
|
||||
{}
|
||||
#define RX_MARK_MASK
|
||||
#define TX_MARK_MASK
|
||||
#define GPIO_H(mask)
|
||||
#define GPIO_L(mask)
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// EMS tx_mode types
|
||||
#define EMS_TXMODE_DEFAULT 1 // Default (was previously known as tx_mode 2)
|
||||
#define EMS_TXMODE_DEFAULT 1 // Default (was previously known as tx_mode 2 in v1.8.x)
|
||||
#define EMS_TXMODE_EMSPLUS 2 // EMS+
|
||||
#define EMS_TXMODE_HT3 3 // Junkers HT3
|
||||
|
||||
@@ -87,11 +27,10 @@
|
||||
#define EMS_ID_GATEWAY 0x48 // KM200 Web Gateway
|
||||
|
||||
// Product IDs
|
||||
#define EMS_PRODUCTID_HEATRONIC 95 // Junkers Heatronic 3 device
|
||||
#define EMS_PRODUCTID_SM10 73 // SM10 solar module
|
||||
#define EMS_PRODUCTID_SM50 162 // SM50 solar module
|
||||
#define EMS_PRODUCTID_SM100 163 // SM100 solar module
|
||||
#define EMS_PRODUCTID_ISM1 101 // Junkers ISM1 solar module
|
||||
#define EMS_PRODUCTID_SM10 73 // SM10 solar module
|
||||
#define EMS_PRODUCTID_SM50 162 // SM50 solar module
|
||||
#define EMS_PRODUCTID_SM100 163 // SM100 solar module
|
||||
#define EMS_PRODUCTID_ISM1 101 // Junkers ISM1 solar module
|
||||
|
||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
||||
#define EMS_MAX_TELEGRAM_LENGTH 32 // max length of a telegram, including CRC, for Rx and Tx.
|
||||
@@ -112,7 +51,7 @@
|
||||
|
||||
// trigger settings to determine if hot tap water or the heating is active
|
||||
#define EMS_BOILER_BURNPOWER_TAPWATER 100
|
||||
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
|
||||
#define EMS_BOILER_SELFLOWTEMP_HEATING 30 // was 70, changed to 30 for https://github.com/proddy/EMS-ESP/issues/193
|
||||
|
||||
// define maximum setable tapwater temperature
|
||||
#define EMS_BOILER_TAPWATER_TEMPERATURE_MAX 60
|
||||
@@ -132,6 +71,7 @@
|
||||
#define EMS_MODELTYPE_HP 4 // success color
|
||||
#define EMS_MODELTYPE_OTHER 5 // no color
|
||||
#define EMS_MODELTYPE_UNKNOWN 6 // no color
|
||||
#define EMS_MODELTYPE_MIXING 7
|
||||
|
||||
#define EMS_MODELTYPE_UNKNOWN_STRING "unknown?" // model type text to use when discovering an unknown device
|
||||
|
||||
@@ -246,7 +186,7 @@ const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||
// where defintions are stored
|
||||
typedef struct {
|
||||
uint8_t product_id;
|
||||
char model_string[50];
|
||||
char model_string[70];
|
||||
} _Boiler_Device;
|
||||
|
||||
typedef struct {
|
||||
@@ -273,6 +213,11 @@ typedef struct {
|
||||
bool write_supported;
|
||||
} _Thermostat_Device;
|
||||
|
||||
typedef struct {
|
||||
uint8_t product_id;
|
||||
char model_string[50];
|
||||
} _Mixing_Device;
|
||||
|
||||
// for consolidating all types
|
||||
typedef struct {
|
||||
uint8_t model_type; // 1=boiler, 2=thermostat, 3=sm, 4=other, 5=unknown
|
||||
@@ -282,6 +227,7 @@ typedef struct {
|
||||
char model_string[50];
|
||||
} _Generic_Device;
|
||||
|
||||
|
||||
/*
|
||||
* Telegram package defintions
|
||||
*/
|
||||
@@ -322,6 +268,7 @@ typedef struct {
|
||||
uint32_t burnStarts; // # burner starts
|
||||
uint32_t burnWorkMin; // Total burner operating time
|
||||
uint32_t heatWorkMin; // Total heat operating time
|
||||
uint16_t switchTemp; // Switch temperature
|
||||
|
||||
// UBAMonitorWWMessage
|
||||
uint16_t wWCurTmp; // Warm Water current temperature
|
||||
@@ -364,6 +311,25 @@ typedef struct {
|
||||
char version[10];
|
||||
} _EMS_Other;
|
||||
|
||||
typedef struct {
|
||||
uint8_t device_id;
|
||||
uint8_t model_id;
|
||||
uint8_t product_id;
|
||||
char version[10];
|
||||
uint8_t hc; // heating circuit 1, 2, 3 or 4
|
||||
bool active; // true if there is data for this HC
|
||||
|
||||
uint16_t flowTemp;
|
||||
uint8_t pumpMod;
|
||||
uint8_t valveStatus;
|
||||
} _EMS_Mixing_HC;
|
||||
|
||||
// Mixer data
|
||||
typedef struct {
|
||||
bool detected;
|
||||
_EMS_Mixing_HC hc[EMS_THERMOSTAT_MAXHC]; // array for the 4 heating circuits
|
||||
} _EMS_Mixing;
|
||||
|
||||
// SM Solar Module - SM10/SM100/ISM1
|
||||
typedef struct {
|
||||
uint8_t device_id; // the device ID of the Solar Module
|
||||
@@ -444,7 +410,7 @@ void ems_setFlowTemp(uint8_t temperature);
|
||||
void ems_setWarmWaterActivated(bool activated);
|
||||
void ems_setWarmTapWaterActivated(bool activated);
|
||||
void ems_setPoll(bool b);
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel, bool silent = false);
|
||||
void ems_setEmsRefreshed(bool b);
|
||||
void ems_setWarmWaterModeComfort(uint8_t comfort);
|
||||
void ems_setModels();
|
||||
@@ -463,6 +429,7 @@ void ems_getSolarModuleValues();
|
||||
bool ems_getPoll();
|
||||
bool ems_getTxEnabled();
|
||||
bool ems_getThermostatEnabled();
|
||||
bool ems_getMixingDeviceEnabled();
|
||||
bool ems_getBoilerEnabled();
|
||||
bool ems_getSolarModuleEnabled();
|
||||
bool ems_getHeatPumpEnabled();
|
||||
@@ -476,7 +443,6 @@ bool ems_getTxCapable();
|
||||
uint32_t ems_getPollFrequency();
|
||||
bool ems_getTxDisabled();
|
||||
|
||||
|
||||
// private functions
|
||||
uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
||||
void _processType(_EMS_RxTelegram * EMS_RxTelegram);
|
||||
@@ -491,5 +457,6 @@ extern _EMS_Thermostat EMS_Thermostat;
|
||||
extern _EMS_SolarModule EMS_SolarModule;
|
||||
extern _EMS_HeatPump EMS_HeatPump;
|
||||
extern _EMS_Other EMS_Other;
|
||||
extern _EMS_Mixing EMS_Mixing;
|
||||
|
||||
extern std::list<_Generic_Device> Devices;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See ChangeLog.md for History
|
||||
* See README.md for Acknowledgments
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -140,9 +139,19 @@
|
||||
|
||||
// Junkers FR10, FW100 (EMS Plus)
|
||||
#define EMS_TYPE_JunkersStatusMessage 0x6F // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_OFFSET_JunkersStatusMessage_mode 0 // current mode
|
||||
#define EMS_OFFSET_JunkersStatusMessage_setpoint 2 // setpoint temp
|
||||
#define EMS_OFFSET_JunkersStatusMessage_curr 4 // current temp
|
||||
|
||||
// MM100 (EMS Plus)
|
||||
#define EMS_TYPE_MMPLUSStatusMessage_HC1 0x01D7 // mixer status HC1
|
||||
#define EMS_TYPE_MMPLUSStatusMessage_HC2 0x01D8 // mixer status HC2
|
||||
#define EMS_TYPE_MMPLUSStatusMessage_HC3 0x01D9 // mixer status HC3
|
||||
#define EMS_TYPE_MMPLUSStatusMessage_HC4 0x01DA // mixer status HC4
|
||||
#define EMS_OFFSET_MMPLUSStatusMessage_flow_temp 3 // flow temperature
|
||||
#define EMS_OFFSET_MMPLUSStatusMessage_pump_mod 5 // pump modulation
|
||||
#define EMS_OFFSET_MMPLUSStatusMessage_valve_status 2 // valve in percent
|
||||
|
||||
|
||||
// Known EMS devices
|
||||
typedef enum {
|
||||
@@ -174,7 +183,10 @@ typedef enum {
|
||||
EMS_MODEL_FR10,
|
||||
EMS_MODEL_FR100,
|
||||
EMS_MODEL_FR110,
|
||||
EMS_MODEL_FW120
|
||||
EMS_MODEL_FW120,
|
||||
|
||||
// mixing devices
|
||||
EMS_MODEL_MM100
|
||||
|
||||
} _EMS_MODEL_ID;
|
||||
|
||||
@@ -184,12 +196,12 @@ typedef enum {
|
||||
const _Boiler_Device Boiler_Devices[] = {
|
||||
|
||||
{72, "MC10 Module"},
|
||||
{123, "Buderus GBx72/Nefit Trendline/Junkers Cerapur"},
|
||||
{115, "Nefit Topline Compact/Buderus GB162"},
|
||||
{123, "Buderus GBx72/Nefit Trendline/Junkers Cerapur/Worcester Greenstar Si"},
|
||||
{115, "Nefit Topline/Buderus GB162"},
|
||||
{203, "Buderus Logamax U122/Junkers Cerapur"},
|
||||
{208, "Buderus Logamax plus/GB192"},
|
||||
{208, "Buderus Logamax plus/GB192/Bosch Condens GC9000"},
|
||||
{64, "Sieger BK15/Nefit Smartline/Buderus GB152"},
|
||||
{EMS_PRODUCTID_HEATRONIC, "Bosch Condens 2500/Junkers Heatronic 3"},
|
||||
{95, "Bosch Condens 2500/Buderus Logamax GB062/Junkers Heatronic 3"},
|
||||
{122, "Nefit Proline"},
|
||||
{170, "Buderus Logano GB212"},
|
||||
{172, "Nefit Enviline"}
|
||||
@@ -197,7 +209,7 @@ const _Boiler_Device Boiler_Devices[] = {
|
||||
};
|
||||
|
||||
/*
|
||||
* Known Solar Module types, device type 0x30
|
||||
* Known Solar Module types, device id is 0x30
|
||||
* format is PRODUCT ID, DESCRIPTION
|
||||
*/
|
||||
const _SolarModule_Device SolarModule_Devices[] = {
|
||||
@@ -209,17 +221,23 @@ const _SolarModule_Device SolarModule_Devices[] = {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Mixing Units
|
||||
* Typically device id is 0x20 or 0x21
|
||||
* format is PRODUCT ID, DESCRIPTION
|
||||
*/
|
||||
const _Mixing_Device Mixing_Devices[] = {
|
||||
{160, "MM100 Mixing Module"},
|
||||
{69, "MM10 Mixer Module"},
|
||||
{159, "MM50 Mixing Module"},
|
||||
};
|
||||
|
||||
// Other EMS devices which are not considered boilers, thermostats or solar modules
|
||||
// format is PRODUCT ID, DEVICE ID, DESCRIPTION
|
||||
const _Other_Device Other_Devices[] = {
|
||||
|
||||
{71, 0x11, "WM10 Switch Module"},
|
||||
|
||||
{69, 0x21, "MM10 Mixer Module"},
|
||||
{160, 0x20, "MM100 Mixing Module"},
|
||||
{160, 0x21, "MM100 Mixing Module"},
|
||||
{159, 0x21, "MM50 Mixing Module"},
|
||||
|
||||
{68, 0x09, "BC10/RFM20 Receiver"},
|
||||
{190, 0x09, "BC10 Base Controller"},
|
||||
{114, 0x09, "BC10 Base Controller"},
|
||||
@@ -237,7 +255,12 @@ const _Other_Device Other_Devices[] = {
|
||||
|
||||
// heatpump, device ID 0x38
|
||||
// format is PRODUCT ID, DEVICE ID, DESCRIPTION
|
||||
const _HeatPump_Device HeatPump_Devices[] = {{252, "HeatPump Module"}};
|
||||
const _HeatPump_Device HeatPump_Devices[] = {
|
||||
|
||||
{252, "HeatPump Module"},
|
||||
{200, "HeatPump Module"}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Known thermostat types and their capabilities
|
||||
@@ -256,7 +279,7 @@ const _Thermostat_Device Thermostat_Devices[] = {
|
||||
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC30, 78, 0x10, "RC30/Moduline 400", EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC300, 158, 0x10, "RC300/RC310/Moduline 3000", EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC300, 158, 0x10, "RC300/RC310/Moduline 3000/Bosch CW400", EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_1010, 165, 0x18, "RC100/Moduline 1010", EMS_THERMOSTAT_WRITE_NO},
|
||||
|
||||
// Sieger
|
||||
|
||||
295
src/ems_utils.cpp
Normal file
295
src/ems_utils.cpp
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Generic utils
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ems_utils.h"
|
||||
|
||||
// convert float to char
|
||||
char * _float_to_char(char * a, float f, uint8_t precision) {
|
||||
long p[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
|
||||
|
||||
char * ret = a;
|
||||
long whole = (long)f;
|
||||
itoa(whole, a, 10);
|
||||
while (*a != '\0')
|
||||
a++;
|
||||
*a++ = '.';
|
||||
long decimal = abs((long)((f - whole) * p[precision]));
|
||||
itoa(decimal, a, 10);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// convert bool to text. bools are stored as bytes
|
||||
char * _bool_to_char(char * s, uint8_t value) {
|
||||
if (value == EMS_VALUE_INT_ON) {
|
||||
strlcpy(s, "on", sizeof(s));
|
||||
} else if (value == EMS_VALUE_INT_OFF) {
|
||||
strlcpy(s, "off", sizeof(s));
|
||||
} else {
|
||||
strlcpy(s, "?", sizeof(s));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// convert short (two bytes) to text string and returns string
|
||||
// decimals: 0 = no division, 1=divide value by 10, 2=divide by 2, 10=divide value by 100
|
||||
// negative values are assumed stored as 1-compliment (https://medium.com/@LeeJulija/how-integers-are-stored-in-memory-using-twos-complement-5ba04d61a56c)
|
||||
char * _short_to_char(char * s, int16_t value, uint8_t decimals) {
|
||||
// remove errors or invalid values
|
||||
if (value == EMS_VALUE_SHORT_NOTSET) {
|
||||
strlcpy(s, "?", 10);
|
||||
return (s);
|
||||
}
|
||||
|
||||
// just print
|
||||
if (decimals == 0) {
|
||||
ltoa(value, s, 10);
|
||||
return (s);
|
||||
}
|
||||
|
||||
// do floating point
|
||||
char s2[10] = {0};
|
||||
// check for negative values
|
||||
if (value < 0) {
|
||||
strlcpy(s, "-", 10);
|
||||
value *= -1; // convert to positive
|
||||
}
|
||||
|
||||
if (decimals == 2) {
|
||||
// divide by 2
|
||||
strlcpy(s, ltoa(value / 2, s2, 10), 10);
|
||||
strlcat(s, ".", 10);
|
||||
strlcat(s, ((value & 0x01) ? "5" : "0"), 10);
|
||||
|
||||
} else {
|
||||
strlcpy(s, ltoa(value / (decimals * 10), s2, 10), 10);
|
||||
strlcat(s, ".", 10);
|
||||
strlcat(s, ltoa(value % (decimals * 10), s2, 10), 10);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// convert short (two bytes) to text string and prints it
|
||||
// decimals: 0 = no division, 1=divide value by 10, 2=divide by 2, 10=divide value by 100
|
||||
char * _ushort_to_char(char * s, uint16_t value, uint8_t decimals) {
|
||||
// remove errors or invalid values
|
||||
if (value == EMS_VALUE_USHORT_NOTSET) {
|
||||
strlcpy(s, "?", 10);
|
||||
return (s);
|
||||
}
|
||||
|
||||
// just print
|
||||
if (decimals == 0) {
|
||||
ltoa(value, s, 10);
|
||||
return (s);
|
||||
}
|
||||
|
||||
// do floating point
|
||||
char s2[10] = {0};
|
||||
|
||||
if (decimals == 2) {
|
||||
// divide by 2
|
||||
strlcpy(s, ltoa(value / 2, s2, 10), 10);
|
||||
strlcat(s, ".", 10);
|
||||
strlcat(s, ((value & 0x01) ? "5" : "0"), 10);
|
||||
|
||||
} else {
|
||||
strlcpy(s, ltoa(value / (decimals * 10), s2, 10), 10);
|
||||
strlcat(s, ".", 10);
|
||||
strlcat(s, ltoa(value % (decimals * 10), s2, 10), 10);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// takes a signed short value (2 bytes), converts to a fraction and prints it
|
||||
// decimals: 0 = no division, 1=divide value by 10, 2=divide by 2, 10=divide value by 100
|
||||
void _renderShortValue(const char * prefix, const char * postfix, int16_t value, uint8_t decimals) {
|
||||
static char buffer[200] = {0};
|
||||
static char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
|
||||
strlcat(buffer, _short_to_char(s, value, decimals), sizeof(buffer));
|
||||
|
||||
if (postfix != nullptr) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// takes a unsigned short value (2 bytes), converts to a fraction and prints it
|
||||
// decimals: 0 = no division, 1=divide value by 10, 2=divide by 2, 10=divide value by 100
|
||||
void _renderUShortValue(const char * prefix, const char * postfix, uint16_t value, uint8_t decimals) {
|
||||
static char buffer[200] = {0};
|
||||
static char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
|
||||
strlcat(buffer, _ushort_to_char(s, value, decimals), sizeof(buffer));
|
||||
|
||||
if (postfix != nullptr) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// convert int (single byte) to text value and returns it
|
||||
char * _int_to_char(char * s, uint8_t value, uint8_t div) {
|
||||
if (value == EMS_VALUE_INT_NOTSET) {
|
||||
strlcpy(s, "?", sizeof(s));
|
||||
return (s);
|
||||
}
|
||||
|
||||
static char s2[5] = {0};
|
||||
|
||||
switch (div) {
|
||||
case 1:
|
||||
itoa(value, s, 10);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strlcpy(s, itoa(value >> 1, s2, 10), 5);
|
||||
strlcat(s, ".", sizeof(s));
|
||||
strlcat(s, ((value & 0x01) ? "5" : "0"), 5);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
strlcpy(s, itoa(value / 10, s2, 10), 5);
|
||||
strlcat(s, ".", sizeof(s));
|
||||
strlcat(s, itoa(value % 10, s2, 10), 5);
|
||||
break;
|
||||
|
||||
default:
|
||||
itoa(value, s, 10);
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// takes an int value (1 byte), converts to a fraction and prints
|
||||
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, uint8_t div) {
|
||||
static char buffer[200] = {0};
|
||||
static char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
|
||||
strlcat(buffer, _int_to_char(s, value, div), sizeof(buffer));
|
||||
|
||||
if (postfix != nullptr) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// takes a long value at prints it to debug log and prints
|
||||
void _renderLongValue(const char * prefix, const char * postfix, uint32_t value) {
|
||||
static char buffer[200] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
|
||||
if (value == EMS_VALUE_LONG_NOTSET) {
|
||||
strlcat(buffer, "?", sizeof(buffer));
|
||||
} else {
|
||||
char s[20] = {0};
|
||||
strlcat(buffer, ltoa(value, s, 10), sizeof(buffer));
|
||||
}
|
||||
|
||||
if (postfix != nullptr) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// takes a bool value at prints it to debug log and prints
|
||||
void _renderBoolValue(const char * prefix, uint8_t value) {
|
||||
static char buffer[200] = {0};
|
||||
static char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
|
||||
strlcat(buffer, _bool_to_char(s, value), sizeof(buffer));
|
||||
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// like itoa but for hex, and quicker
|
||||
char * _hextoa(uint8_t value, char * buffer) {
|
||||
char * p = buffer;
|
||||
byte nib1 = (value >> 4) & 0x0F;
|
||||
byte nib2 = (value >> 0) & 0x0F;
|
||||
*p++ = nib1 < 0xA ? '0' + nib1 : 'A' + nib1 - 0xA;
|
||||
*p++ = nib2 < 0xA ? '0' + nib2 : 'A' + nib2 - 0xA;
|
||||
*p = '\0'; // null terminate just in case
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// for decimals 0 to 99, printed as a 2 char string
|
||||
char * _smallitoa(uint8_t value, char * buffer) {
|
||||
buffer[0] = ((value / 10) == 0) ? '0' : (value / 10) + '0';
|
||||
buffer[1] = (value % 10) + '0';
|
||||
buffer[2] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* for decimals 0 to 999, printed as a string
|
||||
*/
|
||||
char * _smallitoa3(uint16_t value, char * buffer) {
|
||||
buffer[0] = ((value / 100) == 0) ? '0' : (value / 100) + '0';
|
||||
buffer[1] = (((value % 100) / 10) == 0) ? '0' : ((value % 100) / 10) + '0';
|
||||
buffer[2] = (value % 10) + '0';
|
||||
buffer[3] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer and convert to an 8 bit int
|
||||
uint8_t _readIntNumber() {
|
||||
char * numTextPtr = strtok(nullptr, ", \n");
|
||||
if (numTextPtr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return atoi(numTextPtr);
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer and convert to a double
|
||||
float _readFloatNumber() {
|
||||
char * numTextPtr = strtok(nullptr, ", \n");
|
||||
if (numTextPtr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return atof(numTextPtr);
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer as a hex value and convert to a 16 bit int
|
||||
uint16_t _readHexNumber() {
|
||||
char * numTextPtr = strtok(nullptr, ", \n");
|
||||
if (numTextPtr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return (uint16_t)strtol(numTextPtr, 0, 16);
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer
|
||||
char * _readWord() {
|
||||
char * word = strtok(nullptr, ", \n");
|
||||
return word;
|
||||
}
|
||||
33
src/ems_utils.h
Normal file
33
src/ems_utils.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Generic utils
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MyESP.h"
|
||||
#include "ems.h"
|
||||
|
||||
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
|
||||
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
||||
|
||||
char * _float_to_char(char * a, float f, uint8_t precision = 2);
|
||||
char * _bool_to_char(char * s, uint8_t value);
|
||||
char * _short_to_char(char * s, int16_t value, uint8_t decimals = 1);
|
||||
char * _ushort_to_char(char * s, uint16_t value, uint8_t decimals = 1);
|
||||
void _renderShortValue(const char * prefix, const char * postfix, int16_t value, uint8_t decimals = 1);
|
||||
void _renderUShortValue(const char * prefix, const char * postfix, uint16_t value, uint8_t decimals = 1);
|
||||
char * _int_to_char(char * s, uint8_t value, uint8_t div = 1);
|
||||
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, uint8_t div = 1);
|
||||
void _renderLongValue(const char * prefix, const char * postfix, uint32_t value);
|
||||
void _renderBoolValue(const char * prefix, uint8_t value);
|
||||
char * _hextoa(uint8_t value, char * buffer);
|
||||
char * _smallitoa(uint8_t value, char * buffer);
|
||||
char * _smallitoa3(uint16_t value, char * buffer);
|
||||
uint8_t _readIntNumber();
|
||||
float _readFloatNumber();
|
||||
uint16_t _readHexNumber();
|
||||
char * _readWord();
|
||||
@@ -29,7 +29,6 @@ static void emsuart_rx_intr_handler(void * para) {
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
|
||||
length = 0;
|
||||
}
|
||||
GPIO_H(RX_MARK_MASK);
|
||||
// fill IRQ buffer, by emptying Rx FIFO
|
||||
if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
|
||||
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) {
|
||||
@@ -41,7 +40,6 @@ static void emsuart_rx_intr_handler(void * para) {
|
||||
// clear Rx FIFO full and Rx FIFO timeout interrupts
|
||||
USIC(EMSUART_UART) = (1 << UIFF) | (1 << UITO);
|
||||
}
|
||||
GPIO_L(RX_MARK_MASK);
|
||||
|
||||
// BREAK detection = End of EMS data block
|
||||
if (USIS(EMSUART_UART) & ((1 << UIBD))) {
|
||||
@@ -55,7 +53,6 @@ static void emsuart_rx_intr_handler(void * para) {
|
||||
ETS_UART_INTR_ENABLE(); // re-enable UART interrupts
|
||||
|
||||
system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity
|
||||
RX_PULSE(EMSUART_BIT_TIME / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,13 +73,11 @@ static void ICACHE_FLASH_ATTR emsuart_recvTask(os_event_t * events) {
|
||||
}
|
||||
|
||||
if (length == 2) {
|
||||
RX_PULSE(20);
|
||||
// it's a poll or status code, single byte and ok to send on
|
||||
ems_parseTelegram((uint8_t *)pCurrent->buffer, 1);
|
||||
} else if ((length > 4) && (length <= EMS_MAXBUFFERSIZE + 1)) {
|
||||
// ignore double BRK at the end, possibly from the Tx loopback
|
||||
// also telegrams with no data value
|
||||
RX_PULSE(40);
|
||||
ems_parseTelegram((uint8_t *)pCurrent->buffer, length - 1); // transmit EMS buffer, excluding the BRK
|
||||
}
|
||||
}
|
||||
@@ -178,7 +173,7 @@ void ICACHE_FLASH_ATTR emsuart_tx_brk() {
|
||||
uint32_t tmp;
|
||||
|
||||
// must make sure Tx FIFO is empty
|
||||
while (((USS(EMSUART_UART) >> USTXC) & 0xFF) != 0)
|
||||
while (((USS(EMSUART_UART) >> USTXC) & 0xFF))
|
||||
;
|
||||
|
||||
tmp = ((1 << UCRXRST) | (1 << UCTXRST)); // bit mask
|
||||
@@ -188,7 +183,6 @@ void ICACHE_FLASH_ATTR emsuart_tx_brk() {
|
||||
// To create a 11-bit <BRK> we set TXD_BRK bit so the break signal will
|
||||
// automatically be sent when the tx fifo is empty
|
||||
tmp = (1 << UCBRK);
|
||||
GPIO_H(TX_MARK_MASK);
|
||||
USC0(EMSUART_UART) |= (tmp); // set bit
|
||||
|
||||
if (EMS_Sys_Status.emsTxMode == EMS_TXMODE_EMSPLUS) { // EMS+ mode
|
||||
@@ -198,7 +192,6 @@ void ICACHE_FLASH_ATTR emsuart_tx_brk() {
|
||||
}
|
||||
|
||||
USC0(EMSUART_UART) &= ~(tmp); // clear bit
|
||||
GPIO_L(TX_MARK_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -212,22 +205,18 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
}
|
||||
|
||||
if (len) {
|
||||
LA_PULSE(50);
|
||||
|
||||
if (EMS_Sys_Status.emsTxMode == EMS_TXMODE_EMSPLUS) { // With extra tx delay for EMS+
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
TX_PULSE(EMSUART_BIT_TIME / 4);
|
||||
USF(EMSUART_UART) = buf[i];
|
||||
delayMicroseconds(EMSUART_TX_BRK_WAIT); // https://github.com/proddy/EMS-ESP/issues/23#
|
||||
}
|
||||
emsuart_tx_brk(); // send <BRK>
|
||||
emsuart_tx_brk(); // send <BRK>
|
||||
} else if (EMS_Sys_Status.emsTxMode == EMS_TXMODE_HT3) { // Junkers logic by @philrich
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
TX_PULSE(EMSUART_BIT_TIME / 4);
|
||||
USF(EMSUART_UART) = buf[i];
|
||||
|
||||
// just to be safe wait for tx fifo empty (needed?)
|
||||
while (((USS(EMSUART_UART) >> USTXC) & 0xff) != 0)
|
||||
while (((USS(EMSUART_UART) >> USTXC) & 0xff))
|
||||
;
|
||||
|
||||
// wait until bits are sent on wire
|
||||
@@ -256,11 +245,6 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
* We set EMS_Sys_Status.emsTxStatus to EMS_TX_BRK_DETECT and return
|
||||
*
|
||||
*/
|
||||
|
||||
// shorter busy poll...
|
||||
#define EMSUART_BUSY_WAIT (EMSUART_BIT_TIME / 8)
|
||||
#define EMS_TX_TO_CHARS (2 + 20)
|
||||
#define EMS_TX_TO_COUNT ((EMS_TX_TO_CHARS)*10 * 8)
|
||||
uint16_t wdc = EMS_TX_TO_COUNT;
|
||||
ETS_UART_INTR_DISABLE(); // disable rx interrupt
|
||||
|
||||
@@ -270,13 +254,10 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
|
||||
// throw out the telegram...
|
||||
for (uint8_t i = 0; i < len && result == EMS_TX_STATUS_OK;) {
|
||||
GPIO_H(TX_MARK_MASK);
|
||||
|
||||
wdc = EMS_TX_TO_COUNT;
|
||||
volatile uint8_t _usrxc = (USS(EMSUART_UART) >> USRXC) & 0xFF;
|
||||
USF(EMSUART_UART) = buf[i++]; // send each Tx byte
|
||||
// wait for echo from busmaster
|
||||
GPIO_L(TX_MARK_MASK);
|
||||
while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) {
|
||||
delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles...
|
||||
if (--wdc == 0) {
|
||||
@@ -294,18 +275,14 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
// on Rx-BRK (bus collision), we simply enable Rx and leave it
|
||||
// otherwise we send the final Tx-BRK in the loopback and re=enable Rx-INT.
|
||||
// worst case, we'll see an additional Rx-BRK...
|
||||
if (result != EMS_TX_STATUS_OK) {
|
||||
LA_PULSE(200); // mark Tx error
|
||||
} else {
|
||||
if (result == EMS_TX_STATUS_OK) {
|
||||
// neither bus collision nor timeout - send terminating BRK signal
|
||||
GPIO_H(TX_MARK_MASK);
|
||||
if (!(USIS(EMSUART_UART) & (1 << UIBD))) {
|
||||
// no bus collision - send terminating BRK signal
|
||||
USC0(EMSUART_UART) |= (1 << UCLBE) | (1 << UCBRK); // enable loopback & set <BRK>
|
||||
|
||||
// wait until BRK detected...
|
||||
while (!(USIR(EMSUART_UART) & (1 << UIBD))) {
|
||||
// delayMicroseconds(EMSUART_BUSY_WAIT);
|
||||
delayMicroseconds(EMSUART_BIT_TIME);
|
||||
}
|
||||
|
||||
@@ -313,7 +290,6 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
|
||||
phantomBreak = 1;
|
||||
}
|
||||
GPIO_L(TX_MARK_MASK);
|
||||
}
|
||||
ETS_UART_INTR_ENABLE(); // receive anything from FIFO...
|
||||
}
|
||||
|
||||
@@ -19,11 +19,15 @@
|
||||
#define EMSUART_BIT_TIME 104 // bit time @9600 baud
|
||||
|
||||
#define EMSUART_TX_BRK_WAIT 2070 // the BRK from Boiler master is roughly 1.039ms, so accounting for hardware lag using around 2078 (for half-duplex) - 8 (lag)
|
||||
#define EMSUART_TX_WAIT_BYTE EMSUART_BIT_TIME * 10 // Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit)
|
||||
#define EMSUART_TX_WAIT_BRK EMSUART_BIT_TIME * 11 // Time to send a BRK Signal (11 Bit)
|
||||
#define EMSUART_TX_WAIT_GAP EMSUART_BIT_TIME * 7 // Gap between to Bytes
|
||||
#define EMSUART_TX_WAIT_BYTE (EMSUART_BIT_TIME * 10) // Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit)
|
||||
#define EMSUART_TX_WAIT_BRK (EMSUART_BIT_TIME * 11) // Time to send a BRK Signal (11 Bit)
|
||||
#define EMSUART_TX_WAIT_GAP (EMSUART_BIT_TIME * 7) // Gap between to Bytes
|
||||
#define EMSUART_TX_LAG 8
|
||||
|
||||
#define EMSUART_BUSY_WAIT (EMSUART_BIT_TIME / 8)
|
||||
#define EMS_TX_TO_CHARS (2 + 20)
|
||||
#define EMS_TX_TO_COUNT ((EMS_TX_TO_CHARS)*10 * 8)
|
||||
|
||||
#define EMSUART_recvTaskPrio 1
|
||||
#define EMSUART_recvTaskQueueLen 64
|
||||
|
||||
|
||||
@@ -12,33 +12,42 @@
|
||||
|
||||
// TOPICS with _CMD_ are used for receiving commands from an MQTT Broker
|
||||
// EMS-ESP will subscribe to these topics
|
||||
#define TOPIC_GENERIC_CMD "generic_cmd" // for receiving generic system commands via MQTT
|
||||
|
||||
// MQTT for thermostat
|
||||
// these topics can be suffixed with a Heating Circuit number, e.g. thermostat_cmd_temp1 and thermostat_data1
|
||||
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values to MQTT
|
||||
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // temp changes via MQTT
|
||||
#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // mode changes via MQTT
|
||||
#define TOPIC_THERMOSTAT_CMD_DAYTEMP "thermostat_daytemp" // day temp (RC35 specific)
|
||||
#define TOPIC_THERMOSTAT_CMD_NIGHTTEMP "thermostat_nighttemp" // night temp (RC35 specific)
|
||||
#define TOPIC_THERMOSTAT_CMD_HOLIDAYTEMP "thermostat_holidayttemp" // holiday temp (RC35 specific)
|
||||
#define THERMOSTAT_CURRTEMP "thermostat_currtemp" // current temperature
|
||||
#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
|
||||
#define THERMOSTAT_HC "thermostat_hc" // which heating circuit number
|
||||
#define THERMOSTAT_MODE "thermostat_mode" // mode
|
||||
#define THERMOSTAT_DAYTEMP "thermostat_daytemp" // RC35 specific
|
||||
#define THERMOSTAT_NIGHTTEMP "thermostat_nighttemp" // RC35 specific
|
||||
#define THERMOSTAT_HOLIDAYTEMP "thermostat_holidayttemp" // RC35 specific
|
||||
#define THERMOSTAT_HEATINGTYPE "thermostat_heatingtype" // RC35 specific (3=floorheating)
|
||||
#define THERMOSTAT_CIRCUITCALCTEMP "thermostat_circuitcalctemp" // RC35 specific
|
||||
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values to MQTT
|
||||
|
||||
#define TOPIC_THERMOSTAT_CMD "thermostat_cmd" // for receiving thermostat commands via MQTT
|
||||
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // temp changes via MQTT
|
||||
#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // mode changes via MQTT
|
||||
#define TOPIC_THERMOSTAT_CMD_DAYTEMP "daytemp" // day temp (RC35 specific)
|
||||
#define TOPIC_THERMOSTAT_CMD_NIGHTTEMP "nighttemp" // night temp (RC35 specific)
|
||||
#define TOPIC_THERMOSTAT_CMD_HOLIDAYTEMP "holidayttemp" // holiday temp (RC35 specific)
|
||||
|
||||
#define THERMOSTAT_CURRTEMP "currtemp" // current temperature
|
||||
#define THERMOSTAT_SELTEMP "seltemp" // selected temperature
|
||||
#define THERMOSTAT_HC "hc" // which heating circuit number
|
||||
#define THERMOSTAT_MODE "mode" // mode
|
||||
#define THERMOSTAT_DAYTEMP "daytemp" // RC35 specific
|
||||
#define THERMOSTAT_NIGHTTEMP "nighttemp" // RC35 specific
|
||||
#define THERMOSTAT_HOLIDAYTEMP "holidayttemp" // RC35 specific
|
||||
#define THERMOSTAT_HEATINGTYPE "heatingtype" // RC35 specific (3=floorheating)
|
||||
#define THERMOSTAT_CIRCUITCALCTEMP "circuitcalctemp" // RC35 specific
|
||||
|
||||
// MQTT for boiler
|
||||
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values to MQTT
|
||||
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
|
||||
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
|
||||
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values to MQTT
|
||||
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
|
||||
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
|
||||
|
||||
#define TOPIC_BOILER_CMD "boiler_cmd" // for receiving boiler commands via MQTT
|
||||
#define TOPIC_BOILER_CMD_WWACTIVATED "boiler_cmd_wwactivated" // change water on/off
|
||||
#define TOPIC_BOILER_CMD_WWTEMP "boiler_cmd_wwtemp" // wwtemp changes via MQTT
|
||||
#define TOPIC_BOILER_CMD_COMFORT "boiler_cmd_comfort" // ww comfort setting via MQTT
|
||||
#define TOPIC_BOILER_CMD_FLOWTEMP "boiler_cmd_flowtemp" // flowtemp value via MQTT
|
||||
#define TOPIC_BOILER_CMD_COMFORT "comfort" // ww comfort setting via MQTT
|
||||
#define TOPIC_BOILER_CMD_FLOWTEMP "flowtemp" // flowtemp value via MQTT
|
||||
|
||||
// MQTT for mixing device
|
||||
#define TOPIC_MIXING_DATA "mixing_data" // for sending mixing device values to MQTT
|
||||
|
||||
// MQTT for SM10/SM100 Solar Module
|
||||
#define TOPIC_SM_DATA "sm_data" // topic name
|
||||
@@ -57,11 +66,12 @@
|
||||
#define HP_PUMPSPEED "pumpspeed" // pump speed
|
||||
|
||||
// shower time
|
||||
#define TOPIC_SHOWERTIME "showertime" // for sending shower time results
|
||||
#define TOPIC_SHOWER_TIMER "shower_timer" // toggle switch for enabling the shower logic
|
||||
#define TOPIC_SHOWER_ALERT "shower_alert" // toggle switch for enabling the shower alarm logic
|
||||
#define TOPIC_SHOWER_COLDSHOT "shower_coldshot" // used to trigger a coldshot from an MQTT command
|
||||
#define TOPIC_SHOWER_DATA "shower_data" // for sending shower time results
|
||||
#define TOPIC_SHOWER_TIMER "timer" // toggle switch for enabling the shower logic
|
||||
#define TOPIC_SHOWER_ALERT "alert" // toggle switch for enabling the shower alarm logic
|
||||
#define TOPIC_SHOWER_COLDSHOT "coldshot" // used to trigger a coldshot from an MQTT command
|
||||
#define TOPIC_SHOWER_DURATION "duration" // duration of the last shower
|
||||
|
||||
// MQTT for EXTERNAL SENSORS
|
||||
// MQTT for External Sensors
|
||||
#define TOPIC_EXTERNAL_SENSORS "sensors" // for sending sensor values to MQTT
|
||||
#define PAYLOAD_EXTERNAL_SENSORS "temp_%d" // for formatting the payload for each external dallas sensor
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define APP_VERSION "1.9.1"
|
||||
#define APP_VERSION "1.9.2b11"
|
||||
|
||||
BIN
src/websrc/3rdparty/woff/glyphicons-halflings-regular.woff
vendored
Normal file
BIN
src/websrc/3rdparty/woff/glyphicons-halflings-regular.woff
vendored
Normal file
Binary file not shown.
@@ -191,7 +191,7 @@
|
||||
data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
data-content="MQTT Server IP Address"></i></label>
|
||||
<span class="col-xs-9">
|
||||
<input class="form-control input-sm" placeholder="MQTT IP" style="display:inline;max-width:185px"
|
||||
<input class="form-control input-sm" placeholder="IP address" style="display:inline;max-width:185px"
|
||||
id="mqttip" type="text">
|
||||
</span>
|
||||
<br>
|
||||
@@ -201,11 +201,44 @@
|
||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
data-content="MQTT Server port number"></i></label>
|
||||
<span class="col-xs-9">
|
||||
<input class="form-control input-sm" placeholder="MQTT Port" value="" style="display:inline;max-width:185px"
|
||||
<input class="form-control input-sm" placeholder="1883" value="" style="display:inline;max-width:185px"
|
||||
id="mqttport" type="text">
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="row form-group">
|
||||
<label class="col-xs-3">QOS<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
data-content="MQTT QOS 0,1 or 2"></i></label>
|
||||
<span class="col-xs-9">
|
||||
<input class="form-control input-sm" placeholder="1" value="" style="display:inline;max-width:185px"
|
||||
id="mqttqos" type="text">
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="row form-group">
|
||||
<label class="col-xs-3">Keep Alive<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
data-content="MQTT Keep Alive time in seconds"></i></label>
|
||||
<span class="col-xs-9">
|
||||
<input class="form-control input-sm" placeholder="60" value="" style="display:inline;max-width:185px"
|
||||
id="mqttkeepalive" type="text">
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="row form-group">
|
||||
<label class="col-xs-3">Retain<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
data-content="MQTT Retain Flag"></i></label>
|
||||
<div class="col-xs-9">
|
||||
<form>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="1" name="mqttretain">Enabled</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="0" name="mqttretain" checked>Disabled</label>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row form-group">
|
||||
<label class="col-xs-3">Username<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
@@ -231,7 +264,7 @@
|
||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
data-content="MQTT topic prefix (<mqtt base>/<host name>/)"></i></label>
|
||||
<span class="col-xs-9">
|
||||
<input class="form-control input-sm" placeholder="MQTT base" value="" style="display:inline;max-width:185px"
|
||||
<input class="form-control input-sm" placeholder="home" value="" style="display:inline;max-width:185px"
|
||||
id="mqttbase" type="text">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -25,6 +25,9 @@ var config = {
|
||||
"enabled": false,
|
||||
"ip": "",
|
||||
"port": 1883,
|
||||
"qos": 1,
|
||||
"keepalive": 60,
|
||||
"retain": true,
|
||||
"base": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
@@ -171,8 +174,15 @@ function savemqtt() {
|
||||
config.mqtt.heartbeat = true;
|
||||
}
|
||||
|
||||
config.mqtt.retain = false;
|
||||
if (parseInt($("input[name=\"mqttretain\"]:checked").val()) === 1) {
|
||||
config.mqtt.retain = true;
|
||||
}
|
||||
|
||||
config.mqtt.ip = document.getElementById("mqttip").value;
|
||||
config.mqtt.port = parseInt(document.getElementById("mqttport").value);
|
||||
config.mqtt.qos = parseInt(document.getElementById("mqttqos").value);
|
||||
config.mqtt.keepalive = parseInt(document.getElementById("mqttkeepalive").value);
|
||||
config.mqtt.base = document.getElementById("mqttbase").value;
|
||||
config.mqtt.user = document.getElementById("mqttuser").value;
|
||||
config.mqtt.password = document.getElementById("mqttpwd").value;
|
||||
@@ -321,8 +331,14 @@ function listmqtt() {
|
||||
$("input[name=\"mqttheartbeat\"][value=\"1\"]").prop("checked", true);
|
||||
}
|
||||
|
||||
if (config.mqtt.retain) {
|
||||
$("input[name=\"mqttretain\"][value=\"1\"]").prop("checked", true);
|
||||
}
|
||||
|
||||
document.getElementById("mqttip").value = config.mqtt.ip;
|
||||
document.getElementById("mqttport").value = config.mqtt.port;
|
||||
document.getElementById("mqttqos").value = config.mqtt.qos;
|
||||
document.getElementById("mqttkeepalive").value = config.mqtt.keepalive;
|
||||
document.getElementById("mqttbase").value = config.mqtt.base;
|
||||
document.getElementById("mqttuser").value = config.mqtt.user;
|
||||
document.getElementById("mqttpwd").value = config.mqtt.password;
|
||||
|
||||
@@ -1,15 +1,82 @@
|
||||
var gulp = require('gulp');
|
||||
var fs = require('fs');
|
||||
var concat = require('gulp-concat');
|
||||
var gzip = require('gulp-gzip');
|
||||
var flatmap = require('gulp-flatmap');
|
||||
var path = require('path');
|
||||
var htmlmin = require('gulp-htmlmin');
|
||||
var uglify = require('gulp-uglify');
|
||||
var pump = require('pump');
|
||||
/*
|
||||
EMS-ESP web server file system builder
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
gulp.task('myespjs-concat', function () {
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*eslint quotes: ['error', 'single']*/
|
||||
/*eslint-env es6*/
|
||||
|
||||
const gulp = require('gulp');
|
||||
const fs = require('fs');
|
||||
const concat = require('gulp-concat');
|
||||
const gzip = require('gulp-gzip');
|
||||
const flatmap = require('gulp-flatmap');
|
||||
const path = require('path');
|
||||
const htmlmin = require('gulp-htmlmin');
|
||||
const uglify = require('gulp-uglify');
|
||||
const pump = require('pump');
|
||||
const through = require('through2');
|
||||
|
||||
// file name includes extension
|
||||
var buildHeader = function (name) {
|
||||
|
||||
return through.obj(function (source, encoding, callback) {
|
||||
|
||||
var parts = source.path.split(path.sep);
|
||||
var filename = parts[parts.length - 1];
|
||||
var extension = filename.split('.')[1];
|
||||
|
||||
// var safename = name.split('.').join('_');
|
||||
var safename = name.replace(/\.|-/g, "_");
|
||||
|
||||
var destination = "../../src/webh/" + filename + ".h";
|
||||
|
||||
// html files go into root
|
||||
if (extension === "html") {
|
||||
var source = "../../src/websrc/temp/gzipped/" + name + ".gz";
|
||||
} else {
|
||||
var source = "../../src/websrc/temp/gzipped/" + extension + "/" + name + ".gz";
|
||||
}
|
||||
|
||||
console.info('Creating file: ' + filename + ' Extension: ' + extension);
|
||||
|
||||
var wstream = fs.createWriteStream(destination);
|
||||
wstream.on('error', function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
var data = fs.readFileSync(source);
|
||||
|
||||
wstream.write('#define ' + safename + '_gz_len ' + data.length + '\n');
|
||||
wstream.write('const uint8_t ' + safename + '_gz[] PROGMEM = {');
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (i % 1000 == 0) wstream.write("\n");
|
||||
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||
if (i < data.length - 1) wstream.write(',');
|
||||
}
|
||||
|
||||
wstream.write('\n};')
|
||||
wstream.end();
|
||||
|
||||
callback(null, destination);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
gulp.task('myespjs', function () {
|
||||
return gulp.src(['../../src/websrc/myesp.js', '../../src/custom.js'])
|
||||
.pipe(concat({
|
||||
path: 'myesp.js',
|
||||
@@ -18,77 +85,16 @@ gulp.task('myespjs-concat', function () {
|
||||
}
|
||||
}))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/js'))
|
||||
});
|
||||
|
||||
gulp.task('myespjsminify', ["myespjs-concat"], function (cb) {
|
||||
pump([
|
||||
gulp.src('../../src/websrc/temp/js/myesp.js'),
|
||||
uglify(),
|
||||
gulp.dest('../../src/websrc/temp/js/ugly'),
|
||||
],
|
||||
cb
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task("myespjsgz", ["myespjsminify"], function () {
|
||||
return gulp.src("../../src/websrc/temp/js/ugly/myesp.js")
|
||||
.pipe(uglify())
|
||||
.pipe(gulp.dest('../../src/websrc/temp/js/ugly'))
|
||||
.pipe(gzip({
|
||||
append: true
|
||||
}))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/js'));
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/js'))
|
||||
.pipe(buildHeader('myesp.js'));
|
||||
});
|
||||
|
||||
gulp.task('myespjsgzh', ["myespjsgz"], function () {
|
||||
var source = "../../src/websrc/temp/gzipped/js/" + "myesp.js.gz";
|
||||
var destination = "../../src/webh/" + "myesp.js.gz.h";
|
||||
|
||||
var wstream = fs.createWriteStream(destination);
|
||||
wstream.on('error', function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
var data = fs.readFileSync(source);
|
||||
|
||||
wstream.write('#define myesp_js_gz_len ' + data.length + '\n');
|
||||
wstream.write('const uint8_t myesp_js_gz[] PROGMEM = {')
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (i % 1000 == 0) wstream.write("\n");
|
||||
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||
if (i < data.length - 1) wstream.write(',');
|
||||
}
|
||||
|
||||
wstream.write('\n};')
|
||||
wstream.end();
|
||||
});
|
||||
|
||||
gulp.task("scripts", ["scripts-concat"], function () {
|
||||
|
||||
var source = "../../src/websrc/temp/gzipped/js/" + "required.js.gz";
|
||||
var destination = "../../src/webh/" + "required.js.gz.h";
|
||||
|
||||
var wstream = fs.createWriteStream(destination);
|
||||
wstream.on('error', function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
var data = fs.readFileSync(source);
|
||||
|
||||
wstream.write('#define required_js_gz_len ' + data.length + '\n');
|
||||
wstream.write('const uint8_t required_js_gz[] PROGMEM = {')
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (i % 1000 == 0) wstream.write("\n");
|
||||
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||
if (i < data.length - 1) wstream.write(',');
|
||||
}
|
||||
|
||||
wstream.write('\n};')
|
||||
wstream.end();
|
||||
|
||||
});
|
||||
|
||||
gulp.task('scripts-concat', ["myespjsgzh"], function () {
|
||||
gulp.task('requiredjs', function () {
|
||||
return gulp.src(['../../src/websrc/3rdparty/js/jquery-1.12.4.min.js', '../../src/websrc/3rdparty/js/bootstrap-3.3.7.min.js', '../../src/websrc/3rdparty/js/footable-3.1.6.min.js'])
|
||||
.pipe(concat({
|
||||
path: 'required.js',
|
||||
@@ -100,10 +106,12 @@ gulp.task('scripts-concat', ["myespjsgzh"], function () {
|
||||
.pipe(gzip({
|
||||
append: true
|
||||
}))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/js/'));
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/js/'))
|
||||
.pipe(buildHeader('required.js'));
|
||||
});
|
||||
|
||||
gulp.task('styles-concat', function () {
|
||||
|
||||
gulp.task('requiredcss', function () {
|
||||
return gulp.src(['../../src/websrc/3rdparty/css/bootstrap-3.3.7.min.css', '../../src/websrc/3rdparty/css/footable.bootstrap-3.1.6.min.css', '../../src/websrc/3rdparty/css/sidebar.css'])
|
||||
.pipe(concat({
|
||||
path: 'required.css',
|
||||
@@ -115,70 +123,21 @@ gulp.task('styles-concat', function () {
|
||||
.pipe(gzip({
|
||||
append: true
|
||||
}))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/css/'));
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/css/'))
|
||||
.pipe(buildHeader('required.css'));
|
||||
});
|
||||
|
||||
gulp.task("styles", ["styles-concat"], function () {
|
||||
|
||||
var source = "../../src/websrc/temp/gzipped/css/" + "required.css.gz";
|
||||
var destination = "../../src/webh/" + "required.css.gz.h";
|
||||
|
||||
var wstream = fs.createWriteStream(destination);
|
||||
wstream.on('error', function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
var data = fs.readFileSync(source);
|
||||
|
||||
wstream.write('#define required_css_gz_len ' + data.length + '\n');
|
||||
wstream.write('const uint8_t required_css_gz[] PROGMEM = {')
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (i % 1000 == 0) wstream.write("\n");
|
||||
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||
if (i < data.length - 1) wstream.write(',');
|
||||
}
|
||||
|
||||
wstream.write('\n};')
|
||||
wstream.end();
|
||||
|
||||
});
|
||||
|
||||
gulp.task("fontgz", function () {
|
||||
return gulp.src("../../src/websrc/3rdparty/fonts/*.*")
|
||||
.pipe(gulp.dest("../../src/websrc/temp/fonts/"))
|
||||
gulp.task("fontwoff", function () {
|
||||
return gulp.src("../../src/websrc/3rdparty/woff/*.*")
|
||||
.pipe(gulp.dest("../../src/websrc/temp/woff/"))
|
||||
.pipe(gzip({
|
||||
append: true
|
||||
}))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/fonts/'));
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/woff/'))
|
||||
.pipe(buildHeader('glyphicons-halflings-regular.woff'));
|
||||
});
|
||||
|
||||
gulp.task("fonts", ["fontgz"], function () {
|
||||
return gulp.src("../../src/websrc/temp/gzipped/fonts/*.*")
|
||||
.pipe(flatmap(function (stream, file) {
|
||||
var filename = path.basename(file.path);
|
||||
var wstream = fs.createWriteStream("../../src/webh/" + filename + ".h");
|
||||
wstream.on("error", function (err) {
|
||||
gutil.log(err);
|
||||
});
|
||||
var data = file.contents;
|
||||
wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n");
|
||||
wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {")
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (i % 1000 == 0) wstream.write("\n");
|
||||
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||
if (i < data.length - 1) wstream.write(',');
|
||||
}
|
||||
|
||||
wstream.write("\n};")
|
||||
wstream.end();
|
||||
|
||||
return stream;
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('html-concat', function () {
|
||||
gulp.task('myesphtml', function () {
|
||||
return gulp.src(['../../src/websrc/myesp.htm', '../../src/custom.htm'])
|
||||
.pipe(concat({
|
||||
path: 'myesp.html',
|
||||
@@ -191,42 +150,20 @@ gulp.task('html-concat', function () {
|
||||
.pipe(gzip({
|
||||
append: true
|
||||
}))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/'));
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/'))
|
||||
.pipe(buildHeader('myesp.html'));
|
||||
|
||||
});
|
||||
|
||||
gulp.task('htmlsprep', ["html-concat"], function () {
|
||||
gulp.task('indexhtml', function () {
|
||||
return gulp.src('../../src/websrc/index.html')
|
||||
.pipe(htmlmin({ collapseWhitespace: true, minifyJS: true }))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/'))
|
||||
.pipe(gzip({
|
||||
append: true
|
||||
}))
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/'));
|
||||
.pipe(gulp.dest('../../src/websrc/temp/gzipped/'))
|
||||
.pipe(buildHeader('index.html'));
|
||||
});
|
||||
|
||||
gulp.task("htmls", ["htmlsprep"], function () {
|
||||
return gulp.src("../../src/websrc/temp/gzipped/*.gz")
|
||||
.pipe(flatmap(function (stream, file) {
|
||||
var filename = path.basename(file.path);
|
||||
var wstream = fs.createWriteStream("../../src/webh/" + filename + ".h");
|
||||
wstream.on("error", function (err) {
|
||||
gutil.log(err);
|
||||
});
|
||||
var data = file.contents;
|
||||
wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n");
|
||||
wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {")
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (i % 1000 == 0) wstream.write("\n");
|
||||
wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2));
|
||||
if (i < data.length - 1) wstream.write(',');
|
||||
}
|
||||
|
||||
wstream.write("\n};")
|
||||
wstream.end();
|
||||
|
||||
return stream;
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('default', ['scripts', 'styles', "fonts", "htmls"]);
|
||||
gulp.task('default', gulp.parallel('myespjs', 'requiredjs', 'requiredcss', 'fontwoff', 'myesphtml', 'indexhtml'));
|
||||
|
||||
3810
tools/webfilesbuilder/package-lock.json
generated
Normal file
3810
tools/webfilesbuilder/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,18 @@
|
||||
{
|
||||
"name": "uglifier",
|
||||
"version": "0.0.1",
|
||||
"description": "Combine all js and css files into one and gzip them for the EMS-ESP project",
|
||||
"main": "unglify.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "gulp"
|
||||
},
|
||||
"name": "webfilesbuilder",
|
||||
"version": "1.0.0",
|
||||
"description": "Combine all js and css files into one and gzip them",
|
||||
"main": "gulpfile.js",
|
||||
"author": "proddy",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"gulp-cli": "^2.2.0",
|
||||
"private": true,
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-htmlmin": "^4.0.0",
|
||||
"gulp-gzip": "^1.4.2",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-flatmap": "^1.0.2",
|
||||
"gulp-gzip": "^1.4.2",
|
||||
"gulp-htmlmin": "^4.0.0",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"pump": "^3.0.0"
|
||||
},
|
||||
"bin": "node_modules\\gulp\\bin\\gulp.js",
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.1"
|
||||
}
|
||||
}
|
||||
|
||||
27
tools/wsemulator/package-lock.json
generated
Normal file
27
tools/wsemulator/package-lock.json
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "wsemulator",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
|
||||
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0",
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,8 +74,11 @@ var configfile = {
|
||||
},
|
||||
"mqtt": {
|
||||
"enabled": false,
|
||||
"ip": "ip",
|
||||
"port": "port",
|
||||
"ip": "10.10.10.10",
|
||||
"port": 1883,
|
||||
"qos": 1,
|
||||
"keepalive": 60,
|
||||
"retain": true,
|
||||
"base": "base",
|
||||
"user": "user",
|
||||
"password": "password",
|
||||
|
||||
Reference in New Issue
Block a user