1.9.4 - Merge remote-tracking branch 'origin/dev'

This commit is contained in:
Paul
2019-12-15 23:15:18 +01:00
51 changed files with 3155 additions and 3244 deletions

26
.gitignore vendored
View File

@@ -1,36 +1,20 @@
# vscode
.vscode
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/*
# platformio
.pio
.clang_complete
.gcc-flags.json
lib/readme.txt
# web stuff compiled
src/websrc/css/required.css
src/websrc/js/required.js
src/websrc/fonts
src/websrc/gzipped
src/websrc/temp
*.gz.h
src/webh/*.gz.h
# NPM directories
node_modules/
jspm_packages/
.npm
node_modules
# Output of 'npm pack'
*.tgz
# dotenv environment variables file
.env
# OS specific
.DS_Store
# project specfic
.DS_Store
scripts/stackdmp.txt
*.bin
firmware

86
.travis.yml Normal file
View File

@@ -0,0 +1,86 @@
os: linux
language: python
python:
- "2.7"
cache:
directories:
- ${HOME}/.pio
env:
global:
# - BUILDER_TOTAL_THREADS=4
- BUILDER_TOTAL_THREADS=1
- OWNER=${TRAVIS_REPO_SLUG%/*}
- DEV=${OWNER/proddy/dev}
- BRANCH=${TRAVIS_BRANCH/dev/}
- TAG=${DEV}${BRANCH:+_}${BRANCH}
install:
- env | grep TRAVIS
- set -e
- pip install -U platformio
- pio platform update -p
- set +e
branches:
except:
- /^travis-.*-build$/
script:
- ./scripts/build.sh
# - ./scripts/build.sh -p
stages:
- name: Release
# if: type IN (cron, api)
jobs:
include:
- stage: Release
# env: BUILDER_THREAD=0
# - env: BUILDER_THREAD=1
# - env: BUILDER_THREAD=2
# - env: BUILDER_THREAD=3
before_deploy:
- export FIRMWARE_VERSION=$(grep -E '^#define APP_VERSION' ./src/version.h | awk '{print $3}' | sed 's/"//g')
- git tag -f travis-${TAG}-build
- git remote add gh
https://${OWNER}:${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
- git push gh :travis-${TAG}-build || true
- git push -f gh travis-${TAG}-build
- git remote remove gh
deploy:
provider: releases
edge:
branch: master
token: ${GITHUB_TOKEN}
file_glob: true
# file: "firmware/*.bin"
file: "*.bin"
name: latest development build
release_notes:
Version $FIRMWARE_VERSION.
Automatic firmware builds of the current EMS-ESP branch built on $(date +'%F %T %Z') from commit $TRAVIS_COMMIT.
Warning, this is a development build and not fully tested. Use at your own risk.
cleanup: false
prerelease: true
overwrite: true
target_commitish: $TRAVIS_COMMIT
on:
tags: false
branch: dev
notifications:
email:
on_success: change
on_failure: change
webhooks:
urls:
- https://webhooks.gitter.im/e/57e15f7798656d888194
on_success: always
on_failure: never
on_start: never

View File

@@ -5,13 +5,52 @@ 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.4] 2019-12-15
There are breaking changes in this release. Make you sure you adjust the MQTT topics as described in the wiki.
### Added
- Added `publish_always` forcing MQTT topics to be always sent regardless if the data hasn't changed
- Support for DHW once (OneTime water) heating command via MQTT [issue 195](https://github.com/proddy/EMS-ESP/issues/195)
- Added scripts to automatically build firmware images on every Commit/Pull and nightly builds using TravisCI
- Added option to WebUI to also download the latest development build
- Added build scripts for automated CI with TravisCI
- Implemented timezone support and automatic adjustment, also taking daylight saving times into account
- Added `kick` command to reset core services like NTP, Web, Web Sockets
- Added WiFi static IP (setting done in WebUI only)
- `log w <type_id>` for watching a specific telegram type ID
- initial support for EMS+ GB125s and MC110's (https://github.com/proddy/EMS-ESP/wiki/MC110-controller)
- Buderus RFM200 receiver
### Fixed
- Stability for some Wemos clones by decreasing wifi Tx strength and adding small delay
### Changed
- Debug log times show real internet time (if NTP enabled)
- `system` shows local time instead of UTC
- fixed version numbers of libraries in `platformio.ini`
- Normalized Heating modes to `off`, `manual`, `auto`, `night` and `day` to keep generic and not Home Assistant specific (like `heat`)
- Keeping Thermostat day/night modes separate from off/auto/manual, and setting this for the Junkers FR50
- Removed `publish_always`
- Changed NTP interval from 1 hour to 12 hours
- Refactored EMS device library to make it support multi-EMS devices easier (e.g. multiple thermostats)
- `autodetect deep` removed and replaced with `autodetect scan` for scanning known devices.
- MQTT data will be sent when new data arrives. So `publish_time` is used to force a publish at a given frequency (2 mins is default), or 0 for off.
### Removed
- thermostat scan and autodetect deep functions
- removed Event Logging to SPIFFS (worried about wearing). Replaced with SysLog.
## [1.9.3] 2019-10-26
### Added
- Report # TCP dropouts in the `system` command. These could be due to WiFI or MQTT disconnected.
- Added temp and mode to the MQTT `thermostat_cmd` topic
- build scripts for automated CI with TravisCI
### Fixed
@@ -174,7 +213,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- publish dallas external temp sensors to MQTT (thanks @JewelZB)
- shower timer and shower alert options available via set commands
- added support for warm water modes Hot, Comfort and Intelligent [(issue 67)](https://github.com/proddy/EMS-ESP/issues/67)
- added `set publish_time` to set how often to publish MQTT
- added `set publish_time` to set how often to force a publish of MQTT
- support for SM10 Solar Module including MQTT [(issue 77)](https://github.com/proddy/EMS-ESP/issues/77)
- `refresh` command to force a fetch of all known data from the connected EMS devices
@@ -359,7 +398,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Renamed project from EMS-ESP-Boiler to EMS-ESP since it's kinda EMS generic now
- Support for RC20F and RFM20 (https://github.com/proddy/EMS-ESP/issues/18)
- Support for RC20RF and RFM20 (https://github.com/proddy/EMS-ESP/issues/18)
- Moved all EMS device information into a separate file `ems_devices.h` so no longer need to touch `ems.h`
- Telnet commands can be strings now and output is suspended when typing
@@ -380,7 +419,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Scanning known EMS Devices now ignores duplicates (https://github.com/proddy/EMS-ESP/pull/30)
- ServiceCode stored as a two byte char
- Support for RC20F and RFM20 (https://github.com/proddy/EMS-ESP/issues/18)
- Support for RC20RF and RFM20 (https://github.com/proddy/EMS-ESP/issues/18)
## [1.2.3] 2019-01-03

View File

@@ -5,7 +5,7 @@
[![release-date](https://img.shields.io/github/release-date/proddy/EMS-ESP.svg?label=Released)](https://github.com/proddy/EMS-ESP/commits/master)
<br />
[![license](https://img.shields.io/github/license/proddy/EMS-ESP.svg)](LICENSE)
[![travis](https://travis-ci.com/proddy/EMS-ESP.svg?branch=master)](https://travis-ci.com/proddy/EMS-ESP)
[![travis](https://travis-ci.com/proddy/EMS-ESP.svg?branch=dev)](https://travis-ci.com/proddy/EMS-ESP)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/b8880625bdf841d4adb2829732030887)](https://app.codacy.com/app/proddy/EMS-ESP?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP&utm_campaign=Badge_Grade_Settings)
[![downloads](https://img.shields.io/github/downloads/proddy/EMS-ESP/total.svg)](https://github.com/proddy/EMS-ESP/releases)
<br />

View File

@@ -4,6 +4,7 @@
- "auto"
- "heat"
- "off"
mode_command_topic: "home/ems-esp/thermostat_cmd_mode1"
temperature_command_topic: "home/ems-esp/thermostat_cmd_temp1"
@@ -11,7 +12,8 @@
current_temperature_topic: "home/ems-esp/thermostat_data"
temperature_state_topic: "home/ems-esp/thermostat_data"
mode_state_template: "{{ value_json.hc1.mode }}"
mode_state_template: "{% if value_json.hc1.mode in ['manual', 'day'] %} heat {% elif value_json.hc1.mode in ['night', 'off'] %} off {% else %} auto {% endif %}"
current_temperature_template: "{{ value_json.hc1.currtemp }}"
temperature_state_template: "{{ value_json.hc1.seltemp }}"

View File

@@ -20,16 +20,6 @@
# boiler
- platform: mqtt
state_topic: 'home/ems-esp/boiler_data'
name: 'Tap Water'
value_template: '{{ value_json.tapwaterActive }}'
- platform: mqtt
state_topic: 'home/ems-esp/boiler_data'
name: 'Heating'
value_template: '{{ value_json.heatingActive }}'
- platform: mqtt
state_topic: 'home/ems-esp/boiler_data'
name: 'Warm Water selected temperature'

View File

@@ -1,20 +1,26 @@
title: Home
views:
- title: Heating
- id: ems-esp_id0
title: Heating
cards:
- type: entities
- id: ems-esp_id1
type: glance
entities:
- entity: binary_sensor.tap_water
icon: mdi:fire
- entity: binary_sensor.heating
icon: mdi:radiator
- id: ems-esp_id2
type: entities
title: Boiler
show_header_toggle: false
entities:
- sensor.boiler_boottime
- sensor.boiler_updated
- sensor.ems_esp_status
- type: divider
- sensor.warm_water_selected_temperature
- sensor.warm_water_current_temperature
- sensor.warm_water_activated
- sensor.warm_water_3_way_valve
- sensor.warm_water_tapwater_flow_rate
- type: divider
- sensor.boiler_temperature
- sensor.return_temperature
@@ -28,7 +34,8 @@ views:
- sensor.burner_max_power
- sensor.burner_current_power
- type: vertical-stack
- id: ems-esp_id3
type: vertical-stack
cards:
- type: entities
title: Shower Monitor
@@ -49,14 +56,20 @@ views:
service_data:
entity_id: script.shower_coldshot
- type: vertical-stack
- type: history-graph
entities:
- sensor.ems_esp_wifi
- sensor.ems_esp_freemem
- id: ems-esp_id4
type: vertical-stack
cards:
- type: history-graph
entities:
- sensor.pc_room_sensor_temperature
- sensor.current_room_temperature
- sensor.dark_sky_temperature
- type: thermostat
entity: climate.thermostat
- type: thermostat
name: WarmWater
entity: climate.boiler

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -3,7 +3,8 @@
;
[platformio]
default_envs = debug
default_envs = release
;default_envs = debug
[common]
; custom build options are:
@@ -11,25 +12,48 @@ default_envs = debug
; -DTESTS
; -DCRASH
; -DFORCE_SERIAL
; -DMYESP_DEBUG
;custom_flags = -DFORCE_SERIAL -DMYESP_DEBUG
custom_flags =
;general_flags = -DNO_GLOBAL_EEPROM -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -DBEARSSL_SSL_BASIC
# Available lwIP variants (macros):
# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default)
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY = v2 Lower Memory
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH = v2 Higher Bandwidth
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_LOW_MEMORY
# Other flags
# -DVTABLES_IN_FLASH
# -DNO_GLOBAL_EEPROM
# -DBEARSSL_SSL_BASIC
general_flags = -DNO_GLOBAL_EEPROM
# From https://github.com/esp8266/Arduino/blob/master/tools/sdk/ld
# eagle.flash.4m1m.ld = 1019 KB sketch, 1000 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 2052 KB OTA & buffer
# eagle.flash.4m2m.ld = same as above but with 2024 KB SPIFFS
# eagle.flash.4m.ld = same as above but with no SPIFFS storage
build_flags_4m1m = -Wl,-Teagle.flash.4m1m.ld
build_flags = ${common.general_flags} ${common.build_flags_4m1m}
[env]
framework = arduino
;platform = espressif8266@2.2.2 ; arduino core 2.5.2
platform = espressif8266
;platform = https://github.com/platformio/platform-espressif8266#develop
;platform = https://github.com/platformio/platform-espressif8266#feature/stage
lib_deps =
https://github.com/bakercp/CRC32
https://github.com/rlogiacco/CircularBuffer
https://github.com/PaulStoffregen/OneWire
https://github.com/xoseperez/justwifi
https://github.com/marvinroger/async-mqtt-client
https://github.com/xoseperez/eeprom_rotate
https://github.com/bblanchon/ArduinoJson
https://github.com/me-no-dev/ESPAsyncWebServer
https://github.com/me-no-dev/ESPAsyncUDP
https://github.com/me-no-dev/ESPAsyncTCP
https://github.com/me-no-dev/ESPAsyncWebServer#b0c6144
uuid-common@^1.1.0
uuid-log@^2.1.1
uuid-syslog@^2.0.4 ; https://github.com/nomis/mcu-uuid-syslog
JustWifi@2.0.2 ; https://github.com/xoseperez/justwifi
AsyncMqttClient@0.8.2 ; https://github.com/marvinroger/async-mqtt-client
EEPROM_Rotate@0.9.2 ; https://github.com/xoseperez/eeprom_rotate
ArduinoJson@6.13.0 ; https://github.com/bblanchon/ArduinoJson
ESPAsyncTCP@1.2.2 ; https://github.com/me-no-dev/ESPAsyncTCP
upload_speed = 921600
monitor_speed = 115200
@@ -37,35 +61,54 @@ monitor_speed = 115200
;upload_port = /dev/cu.wchusbserial14403
;upload_port = /dev/cu.usbserial-1440
; comment next 2 lines is not using OTA
; comment out this section if using USB and not OTA for firmware uploads
upload_protocol = espota
upload_port = ems-esp.local
# Special build for CI test
#
# These following targets are used by TravisCI to build the firmware versions on Release
# Do not modify
#
[env:travis]
board = esp12e
build_flags = ${common.general_flags}
build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:esp12e]
board = esp12e
build_flags = ${common.general_flags}
build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:d1_mini]
board = d1_mini
build_flags = ${common.general_flags}
build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:nodemcuv2]
board = nodemcuv2
build_flags = ${common.general_flags}
build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:nodemcu]
board = nodemcu
build_flags = ${common.general_flags}
build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
#
# These two targets below (release and debug) can be modified
#
[env:debug]
board = d1_mini
build_type = debug
build_flags = ${common.general_flags} ${common.custom_flags}
build_flags = ${common.build_flags} ${common.custom_flags}
extra_scripts =
pre:scripts/rename_fw.py
pre:scripts/buildweb.py
pre:scripts/pre_script.py
scripts/main_script.py
[env:release]
board = d1_mini
build_type = release
build_flags = ${common.build_flags} ${common.custom_flags}
extra_scripts =
pre:scripts/pre_script.py
scripts/main_script.py

35
scripts/build.sh Normal file → Executable file
View File

@@ -11,16 +11,13 @@ is_git() {
}
stat_bytes() {
echo "size is:"
case "$(uname -s)" in
Darwin) stat -f %z "$1";;
*) stat -c %s "$1";;
esac
filesize=`du -k "$1" | cut -f1;`
echo 'size:' $filesize 'bytes'
}
# Available environments
list_envs() {
grep env: ../platformio.ini | sed 's/\[env:\(.*\)\]/\1/g'
grep env: platformio.ini | sed 's/\[env:\(.*\)\]/\1/g'
}
print_available() {
@@ -59,7 +56,7 @@ set_default_environments() {
}
build_webui() {
cd ../tools/webfilesbuilder
cd ./tools/webfilesbuilder
# Build system uses gulpscript.js to build web interface
if [ ! -e node_modules/gulp/bin/gulp.js ]; then
@@ -73,26 +70,22 @@ build_webui() {
echo "Building web interface..."
node node_modules/gulp/bin/gulp.js || exit
# TODO: do something if webui files are different
# for now, just print in travis log
if ${TRAVIS:-false}; then
git --no-pager diff --stat
fi
cd ../..
}
build_environments() {
echo "--------------------------------------------------------------"
echo "Building firmware images..."
mkdir -p $destination/EMS-ESP-$version
# don't move to firmware folder until Travis fixed (see https://github.com/travis-ci/dpl/issues/846#issuecomment-547157406)
# mkdir -p $destination
for environment in $environments; do
echo -n "* EMS-ESP-$version-$environment.bin --- "
echo "* EMS-ESP-$version-$environment.bin"
platformio run --silent --environment $environment || exit 1
stat_bytes .pio/build/$environment/firmware.bin
[[ "${TRAVIS_BUILD_STAGE_NAME}" = "Test" ]] || \
mv .pio/build/$environment/firmware.bin $destination/EMS-ESP-$version/EMS-ESP-$version-$environment.bin
# mv .pio/build/$environment/firmware.bin $destination/EMS-ESP-$version-$environment.bin
# mv .pio/build/$environment/firmware.bin EMS-ESP-$version-$environment.bin
mv .pio/build/$environment/firmware.bin EMS-ESP-dev-$environment.bin
done
echo "--------------------------------------------------------------"
}
@@ -101,7 +94,7 @@ build_environments() {
####### MAIN
destination=firmware
version_file=../src/version.h
version_file=./src/version.h
version=$(grep -E '^#define APP_VERSION' $version_file | awk '{print $3}' | sed 's/"//g')
if ${TRAVIS:-false}; then
@@ -115,6 +108,8 @@ else
git_tag=
fi
echo $git_tag
if [[ -n $git_tag ]]; then
new_version=${version/-*}
sed -i -e "s@$version@$new_version@" $version_file
@@ -139,7 +134,7 @@ fi
travis=$(list_envs | grep travis | sort)
# get all taregts, excluding travis and debug
available=$(list_envs | grep -Ev -- 'travis|debug' | sort)
available=$(list_envs | grep -Ev -- 'travis|debug|release' | sort)
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS}"
@@ -183,7 +178,7 @@ fi
# for debugging
echo "* git_revision = $git_revision"
echo "* git_tag = $git_tag"
echo "* TRAVIS_EVENT_TYPE = $TRAVIS_EVENT_TYPE"
echo "* TRAVIS_COMMIT = $TRAVIS_COMMIT"
echo "* TRAVIS_TAG = $TRAVIS_TAG"
echo "* TRAVIS_BRANCH = $TRAVIS_BRANCH"
echo "* TRAVIS_BUILD_STAGE_NAME = $TRAVIS_BUILD_STAGE_NAME"

View File

@@ -1,3 +0,0 @@
Import("env")
env.Execute("node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --silent --cwd ./tools/webfilesbuilder")

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env python
from subprocess import call
import os
Import("env")
def code_check(source, target, env):
print("\n** Starting cppcheck...")
call(["cppcheck", os.getcwd()+"/.", "--force", "--enable=all"])
print("\n** Finished cppcheck...\n")
print("\n** Starting cpplint...")
call(["cpplint", "--extensions=ino,cpp,h", "--filter=-legal/copyright,-build/include,-whitespace",
"--linelength=120", "--recursive", "src", "lib/myESP"])
print("\n** Finished cpplint...")
#my_flags = env.ParseFlags(env['BUILD_FLAGS'])
#defines = {k: v for (k, v) in my_flags.get("CPPDEFINES")}
# print defines
# print env.Dump()
# built in targets: (buildprog, size, upload, program, buildfs, uploadfs, uploadfsota)
env.AddPreAction("buildprog", code_check)
# env.AddPostAction(.....)
# see http://docs.platformio.org/en/latest/projectconf/advanced_scripting.html#before-pre-and-after-post-actions
# env.Replace(PROGNAME="firmware_%s" % defines.get("VERSION"))
# env.Replace(PROGNAME="firmware_%s" % env['BOARD'])

37
scripts/main_script.py Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python
Import("env")
class Color(object):
BLACK = '\x1b[1;30m'
RED = '\x1b[1;31m'
GREEN = '\x1b[1;32m'
YELLOW = '\x1b[1;33m'
BLUE = '\x1b[1;34m'
MAGENTA = '\x1b[1;35m'
CYAN = '\x1b[1;36m'
WHITE = '\x1b[1;37m'
LIGHT_GREY = '\x1b[0;30m'
LIGHT_RED = '\x1b[0;31m'
LIGHT_GREEN = '\x1b[0;32m'
LIGHT_YELLOW = '\x1b[0;33m'
LIGHT_BLUE = '\x1b[0;34m'
LIGHT_MAGENTA = '\x1b[0;35m'
LIGHT_CYAN = '\x1b[0;36m'
LIGHT_WHITE = '\x1b[0;37m'
def clr(color, text):
return color + str(text) + '\x1b[0m'
def remove_float_support():
flags = " ".join(env['LINKFLAGS'])
print(clr(Color.BLUE, "** LINKFLAGS = %ss" % flags))
flags = flags.replace("-u _printf_float", "")
flags = flags.replace("-u _scanf_float", "")
newflags = flags.split()
env.Replace(
LINKFLAGS=newflags
)
remove_float_support()

View File

@@ -4,10 +4,25 @@ import os
import re
Import("env")
def build_web(source, target, env):
print("\n** Build web...")
call(["gulp", "-f", os.getcwd()+"/tools/webfilesbuilder/gulpfile.js"])
def build_web():
print("** Building web...")
env.Execute(
"node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --cwd ./tools/webfilesbuilder")
def code_check(source, target, env):
print("** Starting cppcheck...")
call(["cppcheck", os.getcwd()+"/.", "--force", "--enable=all"])
print("\n** Finished cppcheck...\n")
print("\n** Starting cpplint...")
call(["cpplint", "--extensions=ino,cpp,h", "--filter=-legal/copyright,-build/include,-whitespace",
"--linelength=120", "--recursive", "src", "lib/myESP"])
print("\n** Finished cpplint...")
# build web files
build_web()
# extract application details
bag = {}
exprs = [
(re.compile(r'^#define APP_VERSION\s+"(\S+)"'), 'app_version'),
@@ -31,16 +46,9 @@ with open('./src/ems-esp.cpp', 'r') as f:
app_version = bag.get('app_version')
app_name = bag.get('app_name')
app_hostname = bag.get('app_hostname')
board = env['BOARD']
branch = env['PIOENV']
# build the web files
env.AddPreAction("buildprog", build_web)
# build filename, replacing . with _ for the version
#env.Replace(PROGNAME="firmware_%s" % branch + "_" + app_version.replace(".", "_"))
#env.Replace(PROGNAME=app_name + "-" + app_version.replace(".", "_") + "-" + board + "-" + branch)
env.Replace(PROGNAME=app_name + "-" + app_version.replace(".", "_") + "-" + board)
env.Replace(PROGNAME=app_name + "-" +
app_version.replace(".", "_") + "-" + board)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* MyESP.h
* MyESP.h - does all the basics like WiFI/MQTT/NTP/Debug logs etc
*
* Paul Derbyshire - first version December 2018
*/
@@ -9,16 +9,23 @@
#ifndef MyESP_h
#define MyESP_h
#define MYESP_VERSION "1.2.12"
#define MYESP_VERSION "1.2.22"
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client and for ESP32 see https://github.com/marvinroger/async-mqtt-client/issues/127
#include <ESPAsyncUDP.h>
#include <AsyncMqttClient.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
#include <JustWifi.h>
// SysLog
#include <uuid/common.h>
#include <uuid/log.h>
#include <uuid/syslog.h>
static uuid::syslog::SyslogService syslog;
enum MYESP_SYSLOG_LEVEL : uint8_t { MYESP_SYSLOG_INFO, MYESP_SYSLOG_ERROR };
// local libraries
#include "Ntp.h"
#include "TelnetSpy.h" // modified from https://github.com/yasheena/telnetspy
@@ -54,7 +61,8 @@ extern struct rst_info resetInfo;
#define MYESP_CONFIG_FILE "/myesp.json"
#define MYESP_CUSTOMCONFIG_FILE "/customconfig.json"
#define MYESP_EVENTLOG_FILE "/eventlog.json"
#define MYESP_OLD_EVENTLOG_FILE "/eventlog.json" // depreciated
#define MYESP_OLD_CONFIG_FILE "/config.json" // depreciated
#define MYESP_HTTP_USERNAME "admin" // HTTP username
#define MYESP_HTTP_PASSWORD "admin" // default password
@@ -67,6 +75,9 @@ extern struct rst_info resetInfo;
#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
// set to value >0 if the ESP is overheating or there are timing issues. Recommend a value of 1.
#define MYESP_DELAY 1
// MQTT
#define MQTT_PORT 1883 // MQTT port
#define MQTT_RECONNECT_DELAY_MIN 2000 // Try to reconnect in 3 seconds upon disconnection
@@ -92,9 +103,11 @@ extern struct rst_info resetInfo;
#define MQTT_DISCONNECT_EVENT 1
#define MQTT_MESSAGE_EVENT 2
#define MYESP_JSON_MAXSIZE 2000 // for large Dynamic json files
#define MYESP_JSON_MAXSIZE_LARGE 2000 // for large Dynamic json files
#define MYESP_JSON_MAXSIZE_MEDIUM 800 // for medium Dynamic json files
#define MYESP_JSON_MAXSIZE_SMALL 200 // for smaller Static json documents
#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
@@ -102,6 +115,7 @@ extern struct rst_info resetInfo;
// Telnet
#define TELNET_SERIAL_BAUD 115200
#define TELNET_MAX_COMMAND_LENGTH 80 // length of a command
#define TELNET_MAX_BUFFER_LENGTH 700 // max length of telnet string
#define TELNET_EVENT_CONNECT 1
#define TELNET_EVENT_DISCONNECT 0
#define TELNET_EVENT_SHOWCMD 10
@@ -142,10 +156,9 @@ PROGMEM const char * const custom_reset_string[] = {custom_reset_hardware, cus
#define CUSTOM_RESET_FACTORY 5 // Factory reset
#define CUSTOM_RESET_MAX 5
// SPIFFS
// SPIFFS - max allocation is 1000 KB
// https://arduinojson.org/v6/assistant/
#define MYESP_SPIFFS_MAXSIZE_CONFIG 800 // max size for a config file
#define MYESP_SPIFFS_MAXSIZE_EVENTLOG 20000 // max size for the eventlog in bytes
#define MYESP_SPIFFS_MAXSIZE_CONFIG 999 // max size for a config file
// CRASH
/**
@@ -207,14 +220,16 @@ typedef struct {
char description[100];
} command_t;
typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYESP_FSACTION_LOAD } MYESP_FSACTION;
typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYESP_FSACTION_LOAD } MYESP_FSACTION_t;
typedef enum {
MYESP_BOOTSTATUS_POWERON = 0,
MYESP_BOOTSTATUS_BOOTED = 1,
MYESP_BOOTSTATUS_BOOTING = 2,
MYESP_BOOTSTATUS_RESETNEEDED = 3
} MYESP_BOOTSTATUS; // boot messages
} MYESP_BOOTSTATUS_t; // boot messages
typedef enum { MYESP_MQTTLOGTYPE_NONE, MYESP_MQTTLOGTYPE_PUBLISH, MYESP_MQTTLOGTYPE_SUBSCRIBE } MYESP_MQTTLOGTYPE_t;
// for storing all MQTT publish messages
typedef struct {
@@ -222,15 +237,15 @@ typedef struct {
char * topic;
char * payload;
time_t timestamp;
} _MQTT_Log;
} _MQTT_Log_t;
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
typedef std::function<void()> wifi_callback_f;
typedef std::function<void()> ota_callback_f;
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
typedef std::function<void(uint8_t)> telnet_callback_f;
typedef std::function<bool(MYESP_FSACTION, JsonObject json)> fs_loadsave_callback_f;
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_setlist_callback_f;
typedef std::function<bool(MYESP_FSACTION_t, JsonObject json)> fs_loadsave_callback_f;
typedef std::function<bool(MYESP_FSACTION_t, uint8_t, const char *, const char *)> fs_setlist_callback_f;
typedef std::function<void(JsonObject root)> web_callback_f;
// calculates size of an 2d array at compile time
@@ -240,10 +255,7 @@ constexpr size_t ArraySize(T (&)[N]) {
}
#define MYESP_UPTIME_OVERFLOW 4294967295 // Uptime overflow value
// web min and max length of wifi ssid and password
#define MYESP_MAX_STR_LEN 16
#define MYESP_MAX_STR_LEN 16 // web min and max length of wifi ssid and password
#define MYESP_BOOTUP_FLASHDELAY 50 // flash duration for LED at bootup sequence
#define MYESP_BOOTUP_DELAY 2000 // time before we open the window to reset. This is to stop resetting values when uploading firmware via USB
@@ -261,9 +273,6 @@ class MyESP {
MyESP();
~MyESP();
// write event called from within lambda classs
static void _writeEvent(const char * type, const char * src, const char * desc, const char * data);
// wifi
void setWIFICallback(void (*callback)());
void setWIFI(wifi_callback_f callback);
@@ -288,6 +297,9 @@ class MyESP {
bool getUseSerial();
void setUseSerial(bool toggle);
// syslog
void writeLogEvent(const uint8_t type, const char * msg);
// FS
void setSettings(fs_loadsave_callback_f loadsave, fs_setlist_callback_f setlist, bool useSerial = true);
bool fs_saveConfig(JsonObject root);
@@ -295,6 +307,7 @@ class MyESP {
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(int8_t * setting, const char * value, int8_t value_default);
bool fs_setSettingValue(bool * setting, const char * value, bool value_default);
// Web
@@ -308,7 +321,7 @@ class MyESP {
// general
void end();
void loop();
void begin(const char * app_hostname, const char * app_name, const char * app_version, const char * app_url, const char * app_updateurl);
void begin(const char * app_hostname, const char * app_name, const char * app_version, const char * app_url, const char * app_url_api);
void resetESP();
int getWifiQuality();
void showSystemStats();
@@ -317,6 +330,8 @@ class MyESP {
uint32_t getSystemResetReason();
uint8_t getSystemBootStatus();
bool _have_ntp_time;
unsigned long getSystemTime();
void heartbeatPrint();
private:
// mqtt
@@ -328,10 +343,10 @@ class MyESP {
char * _mqttTopic(const char * topic);
// mqtt log
_MQTT_Log MQTT_log[MYESP_MQTTLOG_MAX]; // log for publish and subscribe messages
_MQTT_Log_t MQTT_log[MYESP_MQTTLOG_MAX]; // log for publish and subscribe messages
void _printMQTTLog();
void _addMQTTLog(const char * topic, const char * payload, const uint8_t type);
void _addMQTTLog(const char * topic, const char * payload, const MYESP_MQTTLOGTYPE_t type);
AsyncMqttClient mqttClient; // the MQTT class
uint32_t _mqtt_reconnect_delay;
@@ -359,6 +374,10 @@ class MyESP {
char * _network_ssid;
char * _network_password;
uint8_t _network_wmode;
char * _network_staticip;
char * _network_gatewayip;
char * _network_nmask;
char * _network_dnsip;
bool _wifi_connected;
String _getESPhostname();
@@ -371,7 +390,7 @@ class MyESP {
// crash
void _eeprom_setup();
// telnet & debug
// telnet
TelnetSpy SerialAndTelnet;
void _telnetConnected();
void _telnetDisconnected();
@@ -385,6 +404,9 @@ class MyESP {
telnet_callback_f _telnet_callback_f; // callback for connect/disconnect
bool _changeSetting(uint8_t wc, const char * setting, const char * value);
// syslog
void _syslog_setup();
// fs and settings
void _fs_setup();
bool _fs_loadConfig();
@@ -407,9 +429,11 @@ class MyESP {
char * _app_version;
char * _app_url;
char * _app_updateurl;
char * _app_updateurl_dev;
bool _suspendOutput;
bool _general_serial;
bool _general_log_events;
char * _general_log_ip;
char * _buildTime;
bool _timerequest;
bool _formatreq;
@@ -417,6 +441,7 @@ class MyESP {
char * _getBuildTime();
bool _hasValue(const char * s);
void _printHeap(const char * s);
void _kick();
// reset reason and rtcmem
bool _rtcmem_status;
@@ -455,15 +480,12 @@ class MyESP {
uint32_t _getUsedHeap();
// heartbeat
void _heartbeatCheck(bool force);
void _heartbeatCheck(bool force = false);
// web
web_callback_f _web_callback_f;
const char * _http_username;
// log
void _sendEventLog(uint8_t page);
// web
void _onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len);
void _procMsg(AsyncWebSocketClient * client, size_t sz);
@@ -472,14 +494,12 @@ class MyESP {
void _printScanResult(int networksFound);
void _sendTime();
void _webserver_setup();
void _webRootPage();
void _webResetPage();
void _webResetAllPage();
// ntp
char * _ntp_server;
uint8_t _ntp_interval;
uint16_t _ntp_interval;
bool _ntp_enabled;
uint8_t _ntp_timezone;
};
extern MyESP myESP;

View File

@@ -3,16 +3,86 @@
*/
#include "Ntp.h"
#include "MyESP.h"
char * NtpClient::TimeServerName;
Timezone * NtpClient::tz;
TimeChangeRule * NtpClient::tcr;
time_t NtpClient::syncInterval;
IPAddress NtpClient::timeServer;
AsyncUDP NtpClient::udpListener;
byte NtpClient::NTPpacket[NTP_PACKET_SIZE];
void ICACHE_FLASH_ATTR NtpClient::Ntp(const char * server, time_t syncMins) {
// references:
// https://github.com/filipdanic/compact-timezone-list/blob/master/index.js
// https://github.com/sanohin/google-timezones-json/blob/master/timezones.json
// https://github.com/dmfilipenko/timezones.json/blob/master/timezones.json
// https://home.kpn.nl/vanadovv/time/TZworld.html
// https://www.timeanddate.com/time/zones/
// Australia Eastern Time Zone (Sydney, Melbourne)
TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660}; // UTC + 11 hours
TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600}; // UTC + 10 hours
Timezone ausET(aEDT, aEST);
// Moscow Standard Time (MSK, does not observe DST)
TimeChangeRule msk = {"MSK", Last, Sun, Mar, 1, 180};
Timezone MSK(msk);
// Turkey
TimeChangeRule trt = {"TRT", Last, Sun, Mar, 1, 180};
Timezone TRT(trt);
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
Timezone CE(CEST, CET);
// United Kingdom (London, Belfast)
TimeChangeRule BST = {"BST", Last, Sun, Mar, 1, 60}; // British Summer Time
TimeChangeRule GMT = {"GMT", Last, Sun, Oct, 2, 0}; // Standard Time
Timezone UK(BST, GMT);
// UTC
TimeChangeRule utcRule = {"UTC", Last, Sun, Mar, 1, 0}; // UTC
Timezone UTC(utcRule);
// US Eastern Time Zone (New York, Detroit)
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240}; // Eastern Daylight Time = UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300}; // Eastern Standard Time = UTC - 5 hours
Timezone usET(usEDT, usEST);
// US Central Time Zone (Chicago, Houston)
TimeChangeRule usCDT = {"CDT", Second, Sun, Mar, 2, -300};
TimeChangeRule usCST = {"CST", First, Sun, Nov, 2, -360};
Timezone usCT(usCDT, usCST);
// US Mountain Time Zone (Denver, Salt Lake City)
TimeChangeRule usMDT = {"MDT", Second, Sun, Mar, 2, -360};
TimeChangeRule usMST = {"MST", First, Sun, Nov, 2, -420};
Timezone usMT(usMDT, usMST);
// Arizona is US Mountain Time Zone but does not use DST
Timezone usAZ(usMST);
// US Pacific Time Zone (Las Vegas, Los Angeles)
TimeChangeRule usPDT = {"PDT", Second, Sun, Mar, 2, -420};
TimeChangeRule usPST = {"PST", First, Sun, Nov, 2, -480};
Timezone usPT(usPDT, usPST);
// build index of all timezones
Timezone * timezones[] = {&ausET, &MSK, &CE, &UK, &UTC, &usET, &usCT, &usMT, &usAZ, &usPT, &TRT};
void ICACHE_FLASH_ATTR NtpClient::Ntp(const char * server, time_t syncMins, uint8_t tz_index) {
TimeServerName = strdup(server);
syncInterval = syncMins * 60; // convert to seconds
// check for out of bounds
if (tz_index >= NTP_TIMEZONE_MAX) {
tz_index = NTP_TIMEZONE_DEFAULT;
}
tz = timezones[tz_index]; // set timezone
WiFi.hostByName(TimeServerName, timeServer);
setSyncProvider(getNtpTime);
setSyncInterval(syncInterval);
@@ -38,7 +108,20 @@ time_t ICACHE_FLASH_ATTR NtpClient::getNtpTime() {
unsigned long highWord = word(packet.data()[40], packet.data()[41]);
unsigned long lowWord = word(packet.data()[42], packet.data()[43]);
time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL;
setTime(UnixUTCtime);
time_t adjustedtime = (*tz).toLocal(UnixUTCtime, &tcr);
myESP.myDebug("[NTP] Internet time: %02d:%02d:%02d UTC on %d/%d. Local time: %02d:%02d:%02d %s",
to_hour(UnixUTCtime),
to_minute(UnixUTCtime),
to_second(UnixUTCtime),
to_day(UnixUTCtime),
to_month(UnixUTCtime),
to_hour(adjustedtime),
to_minute(adjustedtime),
to_second(adjustedtime),
tcr->abbrev);
setTime(adjustedtime);
});
}
udpListener.write(NTPpacket, sizeof(NTPpacket));

View File

@@ -10,18 +10,24 @@
#include <ESP8266WiFi.h>
#include <ESPAsyncUDP.h>
#include "TimeLib.h" // customized version of the time library
#include "TimeLib.h" // customized version of the Time library
#include "Timezone.h"
#define NTP_PACKET_SIZE 48 // NTP time is in the first 48 bytes of message
#define NTP_PACKET_SIZE 48 // NTP time is in the first 48 bytes of the message
#define NTP_INTERVAL_DEFAULT 720 // every 12 hours
#define NTP_TIMEZONE_DEFAULT 2 // CE
#define NTP_TIMEZONE_MAX 11
class NtpClient {
public:
void ICACHE_FLASH_ATTR Ntp(const char * server, time_t syncMins);
void ICACHE_FLASH_ATTR Ntp(const char * server, time_t syncMins, uint8_t tz_index);
ICACHE_FLASH_ATTR virtual ~NtpClient();
static char * TimeServerName;
static IPAddress timeServer;
static time_t syncInterval;
static Timezone * tz;
static TimeChangeRule * tcr;
static AsyncUDP udpListener;

View File

@@ -24,16 +24,6 @@ extern "C" {
static TelnetSpy * actualObject = NULL;
static void TelnetSpy_putc(char c) {
if (actualObject) {
actualObject->write(c);
}
}
static void TelnetSpy_ignore_putc(char c) {
;
}
TelnetSpy::TelnetSpy() {
port = TELNETSPY_PORT;
telnetServer = NULL;
@@ -437,19 +427,8 @@ void TelnetSpy::setDebugOutput(bool en) {
if (debugOutput) {
actualObject = this;
#ifdef ESP8266
os_install_putc1((void *)TelnetSpy_putc); // Set system printing (os_printf) to TelnetSpy
system_set_os_print(true);
#endif
} else {
if (actualObject == this) {
#ifdef ESP8266
system_set_os_print(false);
os_install_putc1((void *)TelnetSpy_ignore_putc); // Ignore system printing
#endif
actualObject = NULL;
}
}

View File

@@ -151,13 +151,13 @@
#ifndef TelnetSpy_h
#define TelnetSpy_h
#define TELNETSPY_BUFFER_LEN 3000
#define TELNETSPY_BUFFER_LEN 1000 // was 3000
#define TELNETSPY_MIN_BLOCK_SIZE 64
#define TELNETSPY_COLLECTING_TIME 100
#define TELNETSPY_MAX_BLOCK_SIZE 512
#define TELNETSPY_PING_TIME 1500
#define TELNETSPY_PORT 23
#define TELNETSPY_CAPTURE_OS_PRINT true
#define TELNETSPY_CAPTURE_OS_PRINT false
#define TELNETSPY_WELCOME_MSG "Connection established via Telnet.\n"
#define TELNETSPY_REJECT_MSG "Telnet: Only one connection possible.\n"

View File

@@ -1,3 +1,8 @@
/*
* customized version of Time library, originally Copyright (c) Michael Margolis 2009-2014
* modified by Paul S https://github.com/PaulStoffregen/Time
*/
#include "TimeLib.h"
static tmElements_t tm; // a cache of time elements
@@ -96,6 +101,34 @@ void breakTime(time_t timeInput, tmElements_t & tm) {
tm.Day = time + 1; // day of month
}
// assemble time elements into time_t
time_t makeTime(const tmElements_t & tm) {
int i;
uint32_t seconds;
// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds = tm.Year * (SECS_PER_DAY * 365);
for (i = 0; i < tm.Year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
}
}
// add days for this year, months start from 1
for (i = 1; i < tm.Month; i++) {
if ((i == 2) && LEAP_YEAR(tm.Year)) {
seconds += SECS_PER_DAY * 29;
} else {
seconds += SECS_PER_DAY * monthDays[i - 1]; // monthDay array starts from 0
}
}
seconds += (tm.Day - 1) * SECS_PER_DAY;
seconds += tm.Hour * SECS_PER_HOUR;
seconds += tm.Minute * SECS_PER_MIN;
seconds += tm.Second;
return (time_t)seconds;
}
void refreshCache(time_t t) {
if (t != cacheTime) {
breakTime(t, tm);
@@ -118,6 +151,26 @@ uint8_t to_hour(time_t t) { // the hour for the given time
return tm.Hour;
}
uint8_t to_day(time_t t) { // the day for the given time (0-6)
refreshCache(t);
return tm.Day;
}
uint8_t to_month(time_t t) { // the month for the given time
refreshCache(t);
return tm.Month;
}
uint8_t to_weekday(time_t t) {
refreshCache(t);
return tm.Wday;
}
uint16_t to_year(time_t t) { // the year for the given time
refreshCache(t);
return tm.Year + 1970;
}
void setTime(time_t t) {
sysTime = (uint32_t)t;
nextSyncTime = (uint32_t)t + syncInterval;

View File

@@ -1,3 +1,8 @@
/*
* customized version of Time library, originally Copyright (c) Michael Margolis 2009-2014
* modified by Paul S https://github.com/PaulStoffregen/Time
*/
#ifndef _TimeLib_h
#define _TimeLib_h
@@ -41,5 +46,10 @@ time_t makeTime(const tmElements_t & tm); // convert time e
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
uint8_t to_day(time_t t); // the day for the given time (0-6)
uint8_t to_month(time_t t); // the month for the given time
uint8_t to_weekday(time_t t); // weekday, sunday is day 1
uint16_t to_year(time_t t); // the year for the given time
}
#endif

200
src/Timezone.cpp Normal file
View File

@@ -0,0 +1,200 @@
/*----------------------------------------------------------------------*
* Arduino Timezone Library *
* Jack Christensen Mar 2012 *
* *
* Stripped down for myESP by Paul Derbyshire *
* *
* Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and *
* licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html *
*----------------------------------------------------------------------*/
#include "Timezone.h"
/*----------------------------------------------------------------------*
* Create a Timezone object from the given time change rules. *
*----------------------------------------------------------------------*/
Timezone::Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart)
: m_dst(dstStart)
, m_std(stdStart) {
initTimeChanges();
}
/*----------------------------------------------------------------------*
* Create a Timezone object for a zone that does not observe *
* daylight time. *
*----------------------------------------------------------------------*/
Timezone::Timezone(TimeChangeRule stdTime)
: m_dst(stdTime)
, m_std(stdTime) {
initTimeChanges();
}
/*----------------------------------------------------------------------*
* Convert the given UTC time to local time, standard or *
* daylight time, as appropriate. *
*----------------------------------------------------------------------*/
time_t Timezone::toLocal(time_t utc) {
// recalculate the time change points if needed
if (to_year(utc) != to_year(m_dstUTC))
calcTimeChanges(to_year(utc));
if (utcIsDST(utc))
return utc + m_dst.offset * SECS_PER_MIN;
else
return utc + m_std.offset * SECS_PER_MIN;
}
/*----------------------------------------------------------------------*
* Convert the given UTC time to local time, standard or *
* daylight time, as appropriate, and return a pointer to the time *
* change rule used to do the conversion. The caller must take care *
* not to alter this rule. *
*----------------------------------------------------------------------*/
time_t Timezone::toLocal(time_t utc, TimeChangeRule ** tcr) {
// recalculate the time change points if needed
if (to_year(utc) != to_year(m_dstUTC))
calcTimeChanges(to_year(utc));
if (utcIsDST(utc)) {
*tcr = &m_dst;
return utc + m_dst.offset * SECS_PER_MIN;
} else {
*tcr = &m_std;
return utc + m_std.offset * SECS_PER_MIN;
}
}
/*----------------------------------------------------------------------*
* Convert the given local time to UTC time. *
* *
* WARNING: *
* This function is provided for completeness, but should seldom be *
* needed and should be used sparingly and carefully. *
* *
* Ambiguous situations occur after the Standard-to-DST and the *
* DST-to-Standard time transitions. When changing to DST, there is *
* one hour of local time that does not exist, since the clock moves *
* forward one hour. Similarly, when changing to standard time, there *
* is one hour of local times that occur twice since the clock moves *
* back one hour. *
* *
* This function does not test whether it is passed an erroneous time *
* value during the Local -> DST transition that does not exist. *
* If passed such a time, an incorrect UTC time value will be returned. *
* *
* If passed a local time value during the DST -> Local transition *
* that occurs twice, it will be treated as the earlier time, i.e. *
* the time that occurs before the transistion. *
* *
* Calling this function with local times during a transition interval *
* should be avoided! *
*----------------------------------------------------------------------*/
time_t Timezone::toUTC(time_t local) {
// recalculate the time change points if needed
if (to_year(local) != to_year(m_dstLoc))
calcTimeChanges(to_year(local));
if (locIsDST(local))
return local - m_dst.offset * SECS_PER_MIN;
else
return local - m_std.offset * SECS_PER_MIN;
}
/*----------------------------------------------------------------------*
* Determine whether the given UTC time_t is within the DST interval *
* or the Standard time interval. *
*----------------------------------------------------------------------*/
bool Timezone::utcIsDST(time_t utc) {
// recalculate the time change points if needed
if (to_year(utc) != to_year(m_dstUTC))
calcTimeChanges(to_year(utc));
if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz
return false;
else if (m_stdUTC > m_dstUTC) // northern hemisphere
return (utc >= m_dstUTC && utc < m_stdUTC);
else // southern hemisphere
return !(utc >= m_stdUTC && utc < m_dstUTC);
}
/*----------------------------------------------------------------------*
* Determine whether the given Local time_t is within the DST interval *
* or the Standard time interval. *
*----------------------------------------------------------------------*/
bool Timezone::locIsDST(time_t local) {
// recalculate the time change points if needed
if (to_year(local) != to_year(m_dstLoc))
calcTimeChanges(to_year(local));
if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz
return false;
else if (m_stdLoc > m_dstLoc) // northern hemisphere
return (local >= m_dstLoc && local < m_stdLoc);
else // southern hemisphere
return !(local >= m_stdLoc && local < m_dstLoc);
}
/*----------------------------------------------------------------------*
* Calculate the DST and standard time change points for the given *
* given year as local and UTC time_t values. *
*----------------------------------------------------------------------*/
void Timezone::calcTimeChanges(int yr) {
m_dstLoc = toTime_t(m_dst, yr);
m_stdLoc = toTime_t(m_std, yr);
m_dstUTC = m_dstLoc - m_std.offset * SECS_PER_MIN;
m_stdUTC = m_stdLoc - m_dst.offset * SECS_PER_MIN;
}
/*----------------------------------------------------------------------*
* Initialize the DST and standard time change points. *
*----------------------------------------------------------------------*/
void Timezone::initTimeChanges() {
m_dstLoc = 0;
m_stdLoc = 0;
m_dstUTC = 0;
m_stdUTC = 0;
}
/*----------------------------------------------------------------------*
* Convert the given time change rule to a time_t value *
* for the given year. *
*----------------------------------------------------------------------*/
time_t Timezone::toTime_t(TimeChangeRule r, int yr) {
uint8_t m = r.month; // temp copies of r.month and r.week
uint8_t w = r.week;
if (w == 0) // is this a "Last week" rule?
{
if (++m > 12) // yes, for "Last", go to the next month
{
m = 1;
++yr;
}
w = 1; // and treat as first week of next month, subtract 7 days later
}
// calculate first day of the month, or for "Last" rules, first day of the next month
tmElements_t tm;
tm.Hour = r.hour;
tm.Minute = 0;
tm.Second = 0;
tm.Day = 1;
tm.Month = m;
tm.Year = yr - 1970;
time_t t = makeTime(tm);
// add offset from the first of the month to r.dow, and offset for the given week
t += ((r.dow - to_weekday(t) + 7) % 7 + (w - 1) * 7) * SECS_PER_DAY;
// back up a week if this is a "Last" rule
if (r.week == 0)
t -= 7 * SECS_PER_DAY;
return t;
}
/*----------------------------------------------------------------------*
* Read or update the daylight and standard time rules from RAM. *
*----------------------------------------------------------------------*/
void Timezone::setRules(TimeChangeRule dstStart, TimeChangeRule stdStart) {
m_dst = dstStart;
m_std = stdStart;
initTimeChanges(); // force calcTimeChanges() at next conversion call
}

53
src/Timezone.h Normal file
View File

@@ -0,0 +1,53 @@
/*----------------------------------------------------------------------*
* Arduino Timezone Library *
* Jack Christensen Mar 2012 *
* *
* Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and *
* licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html *
*----------------------------------------------------------------------*/
#ifndef TIMEZONE_H_INCLUDED
#define TIMEZONE_H_INCLUDED
#include <Arduino.h>
#include <TimeLib.h>
// convenient constants for TimeChangeRules
enum week_t { Last, First, Second, Third, Fourth };
enum dow_t { Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat };
enum month_t { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };
// structure to describe rules for when daylight/summer time begins,
// or when standard time begins.
struct TimeChangeRule {
char abbrev[6]; // five chars max
uint8_t week; // First, Second, Third, Fourth, or Last week of the month
uint8_t dow; // day of week, 1=Sun, 2=Mon, ... 7=Sat
uint8_t month; // 1=Jan, 2=Feb, ... 12=Dec
uint8_t hour; // 0-23
int offset; // offset from UTC in minutes
};
class Timezone {
public:
Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart);
Timezone(TimeChangeRule stdTime);
time_t toLocal(time_t utc);
time_t toLocal(time_t utc, TimeChangeRule ** tcr);
time_t toUTC(time_t local);
bool utcIsDST(time_t utc);
bool locIsDST(time_t local);
void setRules(TimeChangeRule dstStart, TimeChangeRule stdStart);
private:
void calcTimeChanges(int yr);
void initTimeChanges();
time_t toTime_t(TimeChangeRule r, int yr);
TimeChangeRule m_dst; // rule for start of dst or summer time for any year
TimeChangeRule m_std; // rule for start of standard time for any year
time_t m_dstUTC; // dst start for given/current year, given in UTC
time_t m_stdUTC; // std time start for given/current year, given in UTC
time_t m_dstLoc; // dst start for given/current year, given in local time
time_t m_stdLoc; // std time start for given/current year, given in local time
};
#endif

View File

@@ -114,9 +114,9 @@
<div class="row form-group">
<label class="col-xs-3">Publish Time<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="How often to send MQTT topics with stats (in seconds)"></i></label>
data-content="How often to send MQTT topics with stats. 0 is automatic. (in seconds)"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" placeholder="120" value="" style="display:inline;max-width:185px"
<input class="form-control input-sm" placeholder="0" value="" style="display:inline;max-width:185px"
id="publish_time" type="text">
</span>
<br>
@@ -131,7 +131,7 @@
<select class="form-control input-sm" id="tx_mode">
<option selected="selected" value="1">1 (EMS generic)</option>
<option value="2">2 (EMS+)</option>
<option value="3">3 (Junkers Heatronic)</option>
<option value="3">3 (Junkers Heatronic HT3)</option>
</select>
</span>
</div>
@@ -257,6 +257,7 @@
</div>
</div>
<div class="row form-group" style="text-align: center;">
<button onclick="refreshEMS()" class="btn btn-info">Refresh</button>
<button onclick="refreshCustomStatus()" class="btn btn-info">Refresh</button>
</div>
<br>
</div>

View File

@@ -8,7 +8,7 @@ var custom_config = {
"listen_mode": false,
"shower_timer": false,
"shower_alert": false,
"publish_time": 120,
"publish_time": 0,
"tx_mode": 1
}
};
@@ -23,15 +23,19 @@ function listcustom() {
if (custom_config.settings.led) {
$("input[name=\"led\"][value=\"1\"]").prop("checked", true);
}
if (custom_config.settings.dallas_parasite) {
$("input[name=\"dallas_parasite\"][value=\"1\"]").prop("checked", true);
}
if (custom_config.settings.listen_mode) {
$("input[name=\"listen_mode\"][value=\"1\"]").prop("checked", true);
}
if (custom_config.settings.shower_timer) {
$("input[name=\"shower_timer\"][value=\"1\"]").prop("checked", true);
}
if (custom_config.settings.shower_alert) {
$("input[name=\"shower_alert\"][value=\"1\"]").prop("checked", true);
}
@@ -95,13 +99,13 @@ function listCustomStats() {
var l = document.createElement("li");
var type = obj[i].type;
var color = "";
if (type === 1) {
if (type === "UBAMaster") {
color = "list-group-item-success";
} else if (type === 2) {
} else if (type === "Thermostat") {
color = "list-group-item-info";
} else if (type === 3) {
} else if (type === "Solar Module") {
color = "list-group-item-warning";
} else if (type === 4) {
} else if (type === "Heat Pump") {
color = "list-group-item-success";
}
l.innerHTML = obj[i].model + " (Version:" + obj[i].version + " ProductID:" + obj[i].productid + " DeviceID:0x" + obj[i].deviceid + ")";

View File

@@ -182,10 +182,10 @@ int16_t DS18::getRawValue(unsigned char index) {
return raw;
}
// return real value as a double
// return real value as a float
// The raw temperature data is in units of sixteenths of a degree, so the value must be divided by 16 in order to convert it to degrees.
double DS18::getValue(unsigned char index) {
double value = (float)getRawValue(index) / 16.0;
float DS18::getValue(unsigned char index) {
float value = (float)getRawValue(index) / 16.0;
return value;
}

View File

@@ -39,7 +39,7 @@ class DS18 {
uint8_t setup(uint8_t gpio, bool parasite);
void loop();
char * getDeviceString(char * s, unsigned char index);
double getValue(unsigned char index);
float getValue(unsigned char index);
int16_t getRawValue(unsigned char index); // raw values, needs / 16
protected:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

213
src/ems.h
View File

@@ -19,29 +19,18 @@
#define EMS_ID_NONE 0x00 // used as a dest in broadcast messages and empty device IDs
// Fixed EMS IDs
#define EMS_ID_ME 0x0B // our device, hardcoded as the "Service Key"
#define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08
#define EMS_ID_SM 0x30 // Solar Module SM10, SM100 and ISM1
#define EMS_ID_HP 0x38 // HeatPump
#define EMS_ID_GATEWAY 0x48 // KM200 Web Gateway
// Product IDs
#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.
// default values for null values
#define EMS_VALUE_INT_ON 1 // boolean true
#define EMS_VALUE_INT_OFF 0 // boolean false
#define EMS_VALUE_BOOL_ON 0x01 // boolean true
#define EMS_VALUE_BOOL_ON2 0xFF // boolean true, EMS sometimes uses 0xFF for TRUE
#define EMS_VALUE_BOOL_OFF 0x00 // boolean false
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit unsigned ints/bytes
#define EMS_VALUE_SHORT_NOTSET -32768 // for 2-byte signed shorts
#define EMS_VALUE_USHORT_NOTSET 0x8000 // for 2-byte unsigned shorts
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
#define EMS_VALUE_BOOL_NOTSET 0xFE // random number that's not 0, 1 or FF
// thermostat specific
#define EMS_THERMOSTAT_MAXHC 4 // max number of heating circuits
@@ -49,6 +38,30 @@
#define EMS_THERMOSTAT_WRITE_YES true
#define EMS_THERMOSTAT_WRITE_NO false
// Device Flags
#define EMS_DEVICE_FLAG_NONE 0 // no flags set
#define EMS_DEVICE_FLAG_SM10 10 // solar module1
#define EMS_DEVICE_FLAG_SM100 11 // solar module2
// group flags specific for thermostats
#define EMS_DEVICE_FLAG_NO_WRITE 0x80 // top bit set if write not supported
#define EMS_DEVICE_FLAG_EASY 1
#define EMS_DEVICE_FLAG_RC10 2
#define EMS_DEVICE_FLAG_RC20 3
#define EMS_DEVICE_FLAG_RC30 4
#define EMS_DEVICE_FLAG_RC35 5
#define EMS_DEVICE_FLAG_RC300 6
#define EMS_DEVICE_FLAG_JUNKERS 7
typedef enum {
EMS_THERMOSTAT_MODE_UNKNOWN,
EMS_THERMOSTAT_MODE_OFF,
EMS_THERMOSTAT_MODE_MANUAL,
EMS_THERMOSTAT_MODE_AUTO,
EMS_THERMOSTAT_MODE_NIGHT,
EMS_THERMOSTAT_MODE_DAY
} _EMS_THERMOSTAT_MODE;
// trigger settings to determine if hot tap water or the heating is active
#define EMS_BOILER_BURNPOWER_TAPWATER 100
#define EMS_BOILER_SELFLOWTEMP_HEATING 30 // was 70, changed to 30 for https://github.com/proddy/EMS-ESP/issues/193
@@ -63,16 +76,6 @@
#define EMS_SYS_DEVICEMAP_LENGTH 15 // size of the 0x07 telegram data part which stores all active EMS devices
// define the model types
// which get rendered to html colors in the web interface in file custom.js in function listCustomStats()
#define EMS_MODELTYPE_BOILER 1 // success color
#define EMS_MODELTYPE_THERMOSTAT 2 // info color
#define EMS_MODELTYPE_SM 3 // warning color
#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
/* EMS UART transfer status */
@@ -105,6 +108,7 @@ typedef enum {
typedef enum {
EMS_SYS_LOGGING_NONE, // no messages
EMS_SYS_LOGGING_RAW, // raw data mode
EMS_SYS_LOGGING_WATCH, // watch a specific type ID
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
EMS_SYS_LOGGING_SOLARMODULE, // only telegrams sent from thermostat
@@ -121,7 +125,8 @@ typedef struct {
uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages
_EMS_SYS_LOGGING emsLogging; // logging
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
uint16_t emsLogging_typeID; // the typeID to watch
uint8_t emsRefreshedFlags; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus
uint32_t emsRxTimestamp; // timestamp of last EMS message received
uint32_t emsPollFrequency; // time between EMS polls
@@ -146,20 +151,19 @@ typedef struct {
uint8_t comparisonValue; // value to compare against during a validate command
uint8_t comparisonOffset; // offset of where the byte is we want to compare too during validation
uint16_t comparisonPostRead; // after a successful write, do a read from this type ID
bool forceRefresh; // should we send to MQTT after a successful Tx?
uint32_t timestamp; // when created
unsigned long timestamp; // when created
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
} _EMS_TxTelegram;
// The Rx receive package
typedef struct {
uint32_t timestamp; // timestamp from millis()
unsigned long timestamp; // timestamp from millis()
uint8_t * telegram; // the full data package
uint8_t data_length; // length in bytes of the data
uint8_t length; // full length of the complete telegram
uint8_t src; // source ID
uint8_t dest; // destination ID
uint16_t type; // type ID as a double byte to support EMS+
uint16_t type; // type ID as a 2-byte to support EMS+
uint8_t offset; // offset
uint8_t * data; // pointer to where telegram data starts
bool emsplus; // true if ems+/ems 2.0
@@ -178,55 +182,60 @@ const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
0, // comparisonValue
0, // comparisonOffset
EMS_ID_NONE, // comparisonPostRead
false, // forceRefresh
0, // timestamp
{0x00} // data
};
// where defintions are stored
typedef struct {
uint8_t product_id;
char model_string[70];
} _Boiler_Device;
// flags for triggering changes when EMS data is received
typedef enum : uint8_t {
EMS_DEVICE_UPDATE_FLAG_NONE = 0,
EMS_DEVICE_UPDATE_FLAG_BOILER = (1 << 0),
EMS_DEVICE_UPDATE_FLAG_THERMOSTAT = (1 << 1),
EMS_DEVICE_UPDATE_FLAG_MIXING = (1 << 2),
EMS_DEVICE_UPDATE_FLAG_SOLAR = (1 << 3),
EMS_DEVICE_UPDATE_FLAG_HEATPUMP = (1 << 4)
} _EMS_DEVICE_UPDATE_FLAG;
typedef struct {
uint8_t product_id;
char model_string[50];
} _SolarModule_Device;
typedef enum : uint8_t {
EMS_DEVICE_TYPE_NONE = 0,
EMS_DEVICE_TYPE_SERVICEKEY,
EMS_DEVICE_TYPE_BOILER,
EMS_DEVICE_TYPE_THERMOSTAT,
EMS_DEVICE_TYPE_MIXING,
EMS_DEVICE_TYPE_SOLAR,
EMS_DEVICE_TYPE_HEATPUMP,
EMS_DEVICE_TYPE_GATEWAY,
EMS_DEVICE_TYPE_OTHER,
EMS_DEVICE_TYPE_SWITCH,
EMS_DEVICE_TYPE_CONTROLLER,
EMS_DEVICE_TYPE_CONNECT,
EMS_DEVICE_TYPE_UNKNOWN
} _EMS_DEVICE_TYPE;
// to store all known EMS devices to date
typedef struct {
uint8_t product_id;
_EMS_DEVICE_TYPE type;
char device_desc[100];
uint8_t flags;
} _EMS_Device;
// to store mapping of device_ids to their string name
typedef struct {
uint8_t device_id;
char model_string[50];
} _Other_Device;
_EMS_DEVICE_TYPE device_type;
char device_type_string[30];
} _EMS_Device_Types;
// for storing all recognised EMS devices
typedef struct {
uint8_t product_id;
char model_string[50];
} _HeatPump_Device;
typedef struct {
uint8_t model_id;
uint8_t product_id;
uint8_t device_id;
char model_string[50];
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
uint8_t product_id;
uint8_t device_id;
char version[10];
char model_string[50];
} _Generic_Device;
_EMS_DEVICE_TYPE device_type; // type (see above)
uint8_t product_id; // product id
uint8_t device_id; // device_id
const char * device_desc_p; // pointer to description string in EMS_Devices table
char version[10]; // the version number XX.XX
bool known; // is this a known device?
} _Detected_Device;
/*
* Telegram package defintions
@@ -234,6 +243,8 @@ typedef struct {
typedef struct {
// settings
uint8_t device_id; // this is typically always 0x08
uint8_t device_flags;
const char * device_desc_p;
uint8_t product_id;
char version[10];
@@ -296,29 +307,18 @@ typedef struct {
*/
typedef struct {
uint8_t device_id; // the device ID of the Heat Pump (e.g. 0x30)
uint8_t model_id; // Solar Module / Heat Pump model
uint8_t device_flags;
const char * device_desc_p;
uint8_t product_id;
char version[10];
uint8_t HPModulation; // heatpump modulation in %
uint8_t HPSpeed; // speed 0-100 %
} _EMS_HeatPump;
// Mixing Module per HC
typedef struct {
uint8_t device_id;
uint8_t model_id;
uint8_t product_id;
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;
@@ -326,17 +326,22 @@ typedef struct {
// Mixer data
typedef struct {
uint8_t device_id;
uint8_t device_flags;
const char * device_desc_p;
uint8_t product_id;
char version[10];
bool detected;
_EMS_Mixing_HC hc[EMS_THERMOSTAT_MAXHC]; // array for the 4 heating circuits
} _EMS_Mixing;
// SM Solar Module - SM10/SM100/ISM1
// Solar Module - SM10/SM100/ISM1
typedef struct {
uint8_t device_id; // the device ID of the Solar Module
uint8_t model_id; // Solar Module
uint8_t device_flags; // Solar Module flags
const char * device_desc_p;
uint8_t product_id;
char version[10];
int16_t collectorTemp; // collector temp
int16_t bottomTemp; // bottom temp
uint8_t pumpModulation; // modulation solar pump
@@ -368,7 +373,8 @@ typedef struct {
// Thermostat data
typedef struct {
uint8_t device_id; // the device ID of the thermostat
uint8_t model_id; // thermostat model
uint8_t device_flags; // thermostat model flags
const char * device_desc_p;
uint8_t product_id;
char version[10];
char datetime[25]; // HH:MM:SS DD/MM/YYYY
@@ -381,20 +387,19 @@ typedef void (*EMS_processType_cb)(_EMS_RxTelegram * EMS_RxTelegram);
// Definition for each EMS type, including the relative callback function
typedef struct {
uint8_t model_id;
uint16_t type; // long to support EMS+ types
const char typeString[50];
_EMS_DEVICE_UPDATE_FLAG device_flag;
uint16_t type;
const char typeString[30];
EMS_processType_cb processType_cb;
} _EMS_Type;
// function definitions
extern void ems_dumpBuffer(const char * prefix, uint8_t * telegram, uint8_t length);
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_dumpBuffer(const char * prefix, uint8_t * telegram, uint8_t length);
void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init();
void ems_doReadCommand(uint16_t type, uint8_t dest, bool forceRefresh = false);
void ems_doReadCommand(uint16_t type, uint8_t dest);
void ems_sendRawTelegram(char * telegram);
void ems_scanDevices();
void ems_printAllDevices();
void ems_printDevices();
uint8_t ems_printDevices_s(char * buffer, uint16_t len);
void ems_printTxQueue();
@@ -402,27 +407,21 @@ void ems_testTelegram(uint8_t test_num);
void ems_startupTelegrams();
bool ems_checkEMSBUSAlive();
void ems_clearDeviceList();
void ems_setThermostatTemp(float temperature, uint8_t hc, uint8_t temptype = 0);
void ems_setThermostatMode(uint8_t mode, uint8_t hc);
void ems_setWarmWaterTemp(uint8_t temperature);
void ems_setFlowTemp(uint8_t temperature);
void ems_setWarmWaterActivated(bool activated);
void ems_setWarmWaterOnetime(bool activated);
void ems_setWarmTapWaterActivated(bool activated);
void ems_setPoll(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel, bool silent = false);
void ems_setEmsRefreshed(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel, uint16_t type_id = 0);
void ems_setWarmWaterModeComfort(uint8_t comfort);
void ems_setModels();
void ems_setTxDisabled(bool b);
void ems_setTxMode(uint8_t mode);
uint8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram);
char * ems_getThermostatDescription(char * buffer, bool name_only = false);
char * ems_getBoilerDescription(char * buffer, bool name_only = false);
char * ems_getSolarModuleDescription(char * buffer, bool name_only = false);
char * ems_getHeatPumpDescription(char * buffer, bool name_only = false);
char * ems_getDeviceDescription(_EMS_DEVICE_TYPE device_type, char * buffer, bool name_only = false);
bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer);
void ems_getThermostatValues();
void ems_getBoilerValues();
void ems_getSolarModuleValues();
@@ -435,13 +434,15 @@ bool ems_getSolarModuleEnabled();
bool ems_getHeatPumpEnabled();
bool ems_getBusConnected();
_EMS_SYS_LOGGING ems_getLogging();
bool ems_getEmsRefreshed();
uint8_t ems_getThermostatModel();
uint8_t ems_getSolarModuleModel();
void ems_discoverModels();
bool ems_getTxCapable();
uint32_t ems_getPollFrequency();
bool ems_getTxDisabled();
void ems_Device_add_flags(unsigned int flags);
bool ems_Device_has_flags(unsigned int flags);
void ems_Device_remove_flags(unsigned int flags);
// private functions
uint8_t _crcCalculator(uint8_t * data, uint8_t len);
@@ -449,6 +450,7 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram);
void _debugPrintPackage(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram, const char * color);
void _ems_clearTxData();
void _removeTxQueue();
uint8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram);
// global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status;
@@ -456,7 +458,6 @@ extern _EMS_Boiler EMS_Boiler;
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;
extern std::list<_Detected_Device> Devices;

View File

@@ -11,6 +11,43 @@
#include "ems.h"
// Fixed EMS Device IDs
#define EMS_ID_ME 0x0B // our device, hardcoded as the "Service Key"
#define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08
#define EMS_ID_SM 0x30 // Solar Module SM10, SM100 and ISM1
#define EMS_ID_HP 0x38 // HeatPump
#define EMS_ID_GATEWAY 0x48 // Gateway e.g. KM200 Web Gateway
#define EMS_ID_MIXING1 0x20 // Mixing
#define EMS_ID_MIXING2 0x21 // Mixing
#define EMS_ID_SWITCH 0x11 // Switch
#define EMS_ID_CONTROLLER 0x09 // Controller
#define EMS_ID_CONNECT1 0x02 // Connect
#define EMS_ID_CONNECT2 0x50 // Connect
#define EMS_ID_THERMOSTAT1 0x10 // Thermostat
#define EMS_ID_THERMOSTAT2 0x17 // Thermostat
#define EMS_ID_THERMOSTAT3 0x18 // Thermostat
// mapping for EMS_Devices_Type
const _EMS_Device_Types EMS_Devices_Types[] = {
{EMS_ID_BOILER, EMS_DEVICE_TYPE_BOILER, "UBAMaster"},
{EMS_ID_THERMOSTAT1, EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_ID_THERMOSTAT2, EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_ID_THERMOSTAT3, EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_ID_SM, EMS_DEVICE_TYPE_SOLAR, "Solar Module"},
{EMS_ID_HP, EMS_DEVICE_TYPE_HEATPUMP, "Heat Pump"},
{EMS_ID_GATEWAY, EMS_DEVICE_TYPE_GATEWAY, "Gateway"},
{EMS_ID_ME, EMS_DEVICE_TYPE_SERVICEKEY, "Me"},
{EMS_ID_NONE, EMS_DEVICE_TYPE_NONE, "All"},
{EMS_ID_MIXING1, EMS_DEVICE_TYPE_MIXING, "Mixing Module"},
{EMS_ID_MIXING2, EMS_DEVICE_TYPE_MIXING, "Mixing Module"},
{EMS_ID_SWITCH, EMS_DEVICE_TYPE_SWITCH, "Switching Module"},
{EMS_ID_CONTROLLER, EMS_DEVICE_TYPE_CONTROLLER, "Controller"},
{EMS_ID_CONNECT1, EMS_DEVICE_TYPE_CONNECT, "Connect"},
{EMS_ID_CONNECT2, EMS_DEVICE_TYPE_CONNECT, "Connect"}
};
/*
* Common Type
*/
@@ -26,13 +63,20 @@
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast
#define EMS_TYPE_UBAParameterWW 0x33
#define EMS_TYPE_UBATotalUptimeMessage 0x14
#define EMS_TYPE_UBAFlags 0x35
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
#define EMS_TYPE_UBAParametersMessage 0x16
#define EMS_TYPE_UBASetPoints 0x1A
#define EMS_TYPE_UBAFunctionTest 0x1D
// EMS+ specific
#define EMS_TYPE_UBAOutdoorTemp 0xD1 // external temp
#define EMS_TYPE_UBAMonitorFast2 0xE4 // Monitor fast for newer EMS+
#define EMS_TYPE_UBAMonitorSlow2 0xE5 // Monitor slow for newer EMS+
#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated
#define EMS_OFFSET_UBAParameterWW_wwOneTime 0x00 // WW OneTime loading
#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode
#define EMS_VALUE_UBAParameterWW_wwComfort_Hot 0x00 // the value for hot
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
@@ -87,7 +131,6 @@
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
// RC35 specific
#define EMS_TYPE_RC35StatusMessage_HC1 0x3E // is an automatic thermostat broadcast giving us temps on HC1
#define EMS_TYPE_RC35StatusMessage_HC2 0x48 // is an automatic thermostat broadcast giving us temps on HC2
@@ -137,9 +180,10 @@
#define EMS_OFFSET_RCPLUSSet_temp_setpoint 8 // temp setpoint, when changing of templevel (in auto) value is reset and set to FF
#define EMS_OFFSET_RCPLUSSet_manual_setpoint 10 // manual setpoint
// Junkers FR10, FW100 (EMS Plus)
// Junkers FR10, FR50, 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_daymode 0 // 3 = day, 2 = night
#define EMS_OFFSET_JunkersStatusMessage_mode 1 // current mode, 1 = manual, 2 = auto
#define EMS_OFFSET_JunkersStatusMessage_setpoint 2 // setpoint temp
#define EMS_OFFSET_JunkersStatusMessage_curr 4 // current temp
@@ -152,147 +196,102 @@
#define EMS_OFFSET_MMPLUSStatusMessage_pump_mod 5 // pump modulation
#define EMS_OFFSET_MMPLUSStatusMessage_valve_status 2 // valve in percent
// Known EMS devices
typedef enum {
EMS_MODEL_NONE, // unset
EMS_MODEL_ALL, // common for all devices
// heatpump
EMS_MODEL_HP,
// solar module
EMS_MODEL_SM,
// boiler
EMS_MODEL_UBA,
// and the thermostats
EMS_MODEL_ES73,
EMS_MODEL_RC10,
EMS_MODEL_RC20,
EMS_MODEL_RC20F,
EMS_MODEL_RC30,
EMS_MODEL_RC35,
EMS_MODEL_EASY,
EMS_MODEL_RC300,
EMS_MODEL_CW100,
EMS_MODEL_1010,
EMS_MODEL_OT,
EMS_MODEL_FW100,
EMS_MODEL_FR10,
EMS_MODEL_FR100,
EMS_MODEL_FR110,
EMS_MODEL_FW120,
// mixing devices
EMS_MODEL_MM100
} _EMS_MODEL_ID;
// EMS types for known boilers. This list will be extended when new devices are recognized.
// The device_id is always 0x08
// format is PRODUCT ID, DESCRIPTION
const _Boiler_Device Boiler_Devices[] = {
{72, "MC10 Module"},
{123, "Buderus GBx72/Nefit Trendline/Junkers Cerapur/Worcester Greenstar Si"},
{133, "Buderus GB125"},
{115, "Nefit Topline/Buderus GB162"},
{203, "Buderus Logamax U122/Junkers Cerapur"},
{208, "Buderus Logamax plus/GB192/Bosch Condens GC9000"},
{64, "Sieger BK13,BK15/Nefit Smartline/Buderus GB1x2"},
{95, "Bosch Condens 2500/Buderus Logamax GB062/Junkers Heatronic 3"},
{122, "Nefit Proline"},
{170, "Buderus Logano GB212"},
{172, "Nefit Enviline"}
};
/*
* Known Solar Module types, device id is 0x30
* format is PRODUCT ID, DESCRIPTION
* Table of all known EMS Devices
* ProductID, DeviceType, Description, Flags
*/
const _SolarModule_Device SolarModule_Devices[] = {
static const _EMS_Device EMS_Devices[] = {
{EMS_PRODUCTID_SM10, "SM10 Solar Module"},
{EMS_PRODUCTID_SM100, "SM100 Solar Module"},
{EMS_PRODUCTID_ISM1, "Junkers ISM1 Solar Module"},
{EMS_PRODUCTID_SM50, "SM50 Solar Module"}
//
// UBA Masters - typically with device_id of 0x08
//
{72, EMS_DEVICE_TYPE_BOILER, "MC10 Module", EMS_DEVICE_FLAG_NONE},
{123, EMS_DEVICE_TYPE_BOILER, "Buderus GBx72/Nefit Trendline/Junkers Cerapur/Worcester Greenstar Si/27i", EMS_DEVICE_FLAG_NONE},
{133, EMS_DEVICE_TYPE_BOILER, "Buderus GB125/Logamatic MC110", EMS_DEVICE_FLAG_NONE},
{115, EMS_DEVICE_TYPE_BOILER, "Nefit Topline/Buderus GB162", EMS_DEVICE_FLAG_NONE},
{203, EMS_DEVICE_TYPE_BOILER, "Buderus Logamax U122/Junkers Cerapur", EMS_DEVICE_FLAG_NONE},
{208, EMS_DEVICE_TYPE_BOILER, "Buderus Logamax plus/GB192/Bosch Condens GC9000", EMS_DEVICE_FLAG_NONE},
{64, EMS_DEVICE_TYPE_BOILER, "Sieger BK13,BK15/Nefit Smartline/Buderus GB1x2", EMS_DEVICE_FLAG_NONE},
{234, EMS_DEVICE_TYPE_BOILER, "Buderus Logamax Plus GB122", EMS_DEVICE_FLAG_NONE},
{95, EMS_DEVICE_TYPE_BOILER, "Bosch Condens 2500/Buderus Logamax GB062/Junkers Cerapur Top/Worcester Greenstar i/Generic HT3", EMS_DEVICE_FLAG_NONE},
{122, EMS_DEVICE_TYPE_BOILER, "Nefit Proline", EMS_DEVICE_FLAG_NONE},
{170, EMS_DEVICE_TYPE_BOILER, "Buderus Logano GB212", EMS_DEVICE_FLAG_NONE},
{172, EMS_DEVICE_TYPE_BOILER, "Nefit Enviline", EMS_DEVICE_FLAG_NONE},
};
//
// Solar Modules - type 0x30
//
{73, EMS_DEVICE_TYPE_SOLAR, "SM10 Solar Module", EMS_DEVICE_FLAG_SM10},
{163, EMS_DEVICE_TYPE_SOLAR, "SM100 Solar Module", EMS_DEVICE_FLAG_SM100},
{101, EMS_DEVICE_TYPE_SOLAR, "Junkers ISM1 Solar Module", EMS_DEVICE_FLAG_SM100},
{162, EMS_DEVICE_TYPE_SOLAR, "SM50 Solar Module", EMS_DEVICE_FLAG_SM100},
/*
* 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"},
};
//
// Mixing Devices - type 0x20 or 0x21
//
{160, EMS_DEVICE_TYPE_MIXING, "MM100 Mixing Module", EMS_DEVICE_FLAG_NONE},
{161, EMS_DEVICE_TYPE_MIXING, "MM200 Mixing Module", EMS_DEVICE_FLAG_NONE},
{69, EMS_DEVICE_TYPE_MIXING, "MM10 Mixer Module", EMS_DEVICE_FLAG_NONE},
{159, EMS_DEVICE_TYPE_MIXING, "MM50 Mixing Module", EMS_DEVICE_FLAG_NONE},
{79, EMS_DEVICE_TYPE_MIXING, "MM100 Mixer Module", EMS_DEVICE_FLAG_NONE},
{80, EMS_DEVICE_TYPE_MIXING, "MM200 Mixer Module", EMS_DEVICE_FLAG_NONE},
{78, EMS_DEVICE_TYPE_MIXING, "MM400 Mixer Module", EMS_DEVICE_FLAG_NONE},
// Other EMS devices which are not considered boilers, thermostats or solar modules
// format is PRODUCT ID, DEVICE ID, DESCRIPTION
const _Other_Device Other_Devices[] = {
//
// HeatPump - type 0x38
//
{252, EMS_DEVICE_TYPE_HEATPUMP, "HeatPump Module", EMS_DEVICE_FLAG_NONE},
{200, EMS_DEVICE_TYPE_HEATPUMP, "HeatPump Module", EMS_DEVICE_FLAG_NONE},
{71, 0x11, "WM10 Switch Module"},
//
// Other devices, like 0x11 for Switching, 0x09 for controllers, 0x02 for Connect, 0x48 for Gateway
//
{71, EMS_DEVICE_TYPE_SWITCH, "WM10 Switch Module", EMS_DEVICE_FLAG_NONE}, // 0x11
{68, EMS_DEVICE_TYPE_CONTROLLER, "BC10/RFM20 Receiver", EMS_DEVICE_FLAG_NONE}, // 0x09
{218, EMS_DEVICE_TYPE_CONTROLLER, "Junkers M200/Buderus RFM200 Receiver", EMS_DEVICE_FLAG_NONE}, // 0x50
{190, EMS_DEVICE_TYPE_CONTROLLER, "BC10 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{114, EMS_DEVICE_TYPE_CONTROLLER, "BC10 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{125, EMS_DEVICE_TYPE_CONTROLLER, "BC25 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{169, EMS_DEVICE_TYPE_CONTROLLER, "BC40 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{152, EMS_DEVICE_TYPE_CONTROLLER, "Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{95, EMS_DEVICE_TYPE_CONTROLLER, "HT3 Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{230, EMS_DEVICE_TYPE_CONTROLLER, "BC Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{205, EMS_DEVICE_TYPE_CONNECT, "Nefit Moduline Easy Connect", EMS_DEVICE_FLAG_NONE}, // 0x02
{206, EMS_DEVICE_TYPE_CONNECT, "Bosch Easy Connect", EMS_DEVICE_FLAG_NONE}, // 0x02
{171, EMS_DEVICE_TYPE_CONNECT, "EMS-OT OpenTherm converter", EMS_DEVICE_FLAG_NONE}, // 0x02
{189, EMS_DEVICE_TYPE_GATEWAY, "Web Gateway KM200", EMS_DEVICE_FLAG_NONE}, // 0x48
{68, 0x09, "BC10/RFM20 Receiver"},
{190, 0x09, "BC10 Base Controller"},
{114, 0x09, "BC10 Base Controller"},
{125, 0x09, "BC25 Base Controller"},
{169, 0x09, "BC40 Base Controller"},
{152, 0x09, "Junkers Controller"},
{230, 0x09, "BC Base Controller"},
{205, 0x02, "Nefit Moduline Easy Connect"},
{206, 0x02, "Bosch Easy Connect"},
{171, 0x02, "EMS-OT OpenTherm converter"},
{189, EMS_ID_GATEWAY, "Web Gateway KM200"}
};
// heatpump, device ID 0x38
// format is PRODUCT ID, DEVICE ID, DESCRIPTION
const _HeatPump_Device HeatPump_Devices[] = {
{252, "HeatPump Module"},
{200, "HeatPump Module"}
};
/*
* Known thermostat types and their capabilities
* format is MODEL_ID, PRODUCT ID, DEVICE ID, DESCRIPTION
*/
const _Thermostat_Device Thermostat_Devices[] = {
//
// Thermostats, typically device id of 0x10, 0x17 and 0x18
//
// Easy devices - not currently supporting write operations
{EMS_MODEL_EASY, 202, 0x18, "Logamatic TC100/Nefit Moduline Easy", EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_EASY, 203, 0x18, "Bosch EasyControl CT200", EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_CW100, 157, 0x18, "Bosch CW100", EMS_THERMOSTAT_WRITE_NO},
{202, EMS_DEVICE_TYPE_THERMOSTAT, "Logamatic TC100/Nefit Moduline Easy", EMS_DEVICE_FLAG_EASY | EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{203, EMS_DEVICE_TYPE_THERMOSTAT, "Bosch EasyControl CT200", EMS_DEVICE_FLAG_EASY | EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{157, EMS_DEVICE_TYPE_THERMOSTAT, "Buderus RC200/Bosch CW100/Junkers CW100", EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Buderus/Nefit
{EMS_MODEL_RC10, 79, 0x17, "RC10/Moduline 100", EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20, 77, 0x17, "RC20/Moduline 300", EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC30, 67, 0x10, "RC30", 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/Bosch CW400", EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_1010, 165, 0x18, "RC100/Moduline 1010", EMS_THERMOSTAT_WRITE_NO},
{79, EMS_DEVICE_TYPE_THERMOSTAT, "RC10/Moduline 100", EMS_DEVICE_FLAG_RC10}, // 0x17
{77, EMS_DEVICE_TYPE_THERMOSTAT, "RC20/Moduline 300", EMS_DEVICE_FLAG_RC20}, // 0x17
{93, EMS_DEVICE_TYPE_THERMOSTAT, "RC20RF", EMS_DEVICE_FLAG_RC20}, // 0x18
{67, EMS_DEVICE_TYPE_THERMOSTAT, "RC30", EMS_DEVICE_FLAG_RC30}, // 0x10
{78, EMS_DEVICE_TYPE_THERMOSTAT, "RC30/Moduline 400", EMS_DEVICE_FLAG_RC30}, // 0x10
{86, EMS_DEVICE_TYPE_THERMOSTAT, "RC35", EMS_DEVICE_FLAG_RC35}, // 0x10
{158, EMS_DEVICE_TYPE_THERMOSTAT, "RC300/RC310/Moduline 3000/Bosch CW400/W-B Sense II", EMS_DEVICE_FLAG_RC300}, // 0x10
{165, EMS_DEVICE_TYPE_THERMOSTAT, "RC100/Moduline 1010", EMS_DEVICE_FLAG_RC300 | EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Sieger
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_WRITE_YES},
{076, EMS_DEVICE_TYPE_THERMOSTAT, "Sieger ES73", EMS_DEVICE_FLAG_RC35}, // 0x10
// Junkers
{EMS_MODEL_FW100, 105, 0x10, "Junkers FW100", EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_FR10, 111, 0x18, "Junkers FR10", EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_FR100, 105, 0x18, "Junkers FR100", EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_FR110, 108, 0x18, "Junkers FR110", EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_FW120, 192, 0x10, "Junkers FW120", EMS_THERMOSTAT_WRITE_NO}
{105, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW100", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{106, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW200", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{107, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR100", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{108, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR110", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{111, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR10", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{191, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR120", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{192, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW120", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{147, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR50", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE} // 0x10, cannot write
};

View File

@@ -25,11 +25,11 @@ char * _float_to_char(char * a, float f, uint8_t precision) {
// convert bool to text. bools are stored as bytes
char * _bool_to_char(char * s, uint8_t value) {
if (value == EMS_VALUE_INT_ON) {
if ((value == EMS_VALUE_BOOL_ON) || (value == EMS_VALUE_BOOL_ON2)) {
strlcpy(s, "on", sizeof(s));
} else if (value == EMS_VALUE_INT_OFF) {
} else if (value == EMS_VALUE_BOOL_OFF) {
strlcpy(s, "off", sizeof(s));
} else {
} else { // EMS_VALUE_BOOL_NOTSET
strlcpy(s, "?", sizeof(s));
}
return s;
@@ -51,22 +51,24 @@ char * _short_to_char(char * s, int16_t value, uint8_t decimals) {
return (s);
}
// do floating point
char s2[10] = {0};
// check for negative values
if (value < 0) {
strlcpy(s, "-", 10);
value *= -1; // convert to positive
} else {
strlcpy(s, "", 10);
}
// do floating point
char s2[10] = {0};
if (decimals == 2) {
// divide by 2
strlcpy(s, ltoa(value / 2, s2, 10), 10);
strlcat(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, ltoa(value / (decimals * 10), s2, 10), 10);
strlcat(s, ".", 10);
strlcat(s, ltoa(value % (decimals * 10), s2, 10), 10);
}
@@ -108,7 +110,7 @@ char * _ushort_to_char(char * s, uint16_t value, uint8_t decimals) {
}
// 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
// decimals: 0=no division, 1=divide value by 10 (default), 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};
@@ -270,7 +272,7 @@ uint8_t _readIntNumber() {
return atoi(numTextPtr);
}
// used to read the next string from an input buffer and convert to a double
// used to read the next string from an input buffer and convert to a float
float _readFloatNumber() {
char * numTextPtr = strtok(nullptr, ", \n");
if (numTextPtr == nullptr) {

View File

@@ -13,7 +13,7 @@
#define EMSUART_CONFIG 0x1C // 8N1 (8 bits, no stop bits, 1 parity)
#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit
#define EMS_MAXBUFFERS 5 // buffers for circular filling to avoid collisions
#define EMS_MAXBUFFERS 3 // buffers for circular filling to avoid collisions
#define EMS_MAXBUFFERSIZE (EMS_MAX_TELEGRAM_LENGTH + 2) // max size of the buffer. EMS packets are max 32 bytes, plus extra 2 for BRKs
#define EMSUART_BIT_TIME 104 // bit time @9600 baud

View File

@@ -44,6 +44,7 @@
#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_WWONETIME "boiler_cmd_wwonetime" // warm warter one time loading
#define TOPIC_BOILER_CMD_WWTEMP "boiler_cmd_wwtemp" // wwtemp changes via MQTT
#define TOPIC_BOILER_CMD_COMFORT "comfort" // ww comfort setting via MQTT
#define TOPIC_BOILER_CMD_FLOWTEMP "flowtemp" // flowtemp value via MQTT

View File

@@ -54,7 +54,8 @@ static const char * TEST_DATA[] = {
"88 00 19 00 00 DC 80 00 80 00 FF FF 00 00 00 21 9A 06 E1 7C 00 00 00 06 C2 13 00 1E 90 80 00", // test 49 - check max length
"30 00 FF 00 02 8E 00 00 41 82 00 00 28 36 00 00 82 21", // test 50 - SM100
"10 00 FF 08 01 B9 26", // test 51 - EMS+ 0x1B9 set temp
"10 00 F7 00 FF 01 B9 21 E9" // test 52 - EMS+ 0x1B9 F7 test
"10 00 F7 00 FF 01 B9 21 E9", // test 52 - EMS+ 0x1B9 F7 test
"08 00 D1 00 00 80" // test 53 - outdoor temp
};

View File

@@ -1 +1 @@
#define APP_VERSION "1.9.3"
#define APP_VERSION "1.9.4"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -52,9 +52,6 @@
</li>
</ul>
</li>
<li>
<a href="#" id="eventlog"><i class="glyphicon glyphicon-transfer"></i>Event Log</a>
</li>
<li>
<a href="#" id="backup"><i class="glyphicon glyphicon-floppy-disk"></i>Backup & Restore</a>
</li>
@@ -194,7 +191,10 @@
</div>
<div class="modal-body">
<div>
<h4>Latest Stable Release</h4>
<div>
<button id="updateb" onclick="switchfirmware()" type="submit" class="btn btn-success btn-sm pull-right">Release</button>
</div>
<div id="onlineupdate">
<h5 id=releasehead></h5>
<div style="clear:both;">

View File

@@ -100,7 +100,6 @@
</span>
<br>
</div>
<div class="row form-group">
<label class="col-xs-3">Serial Port<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
@@ -115,11 +114,10 @@
</div>
<br>
</div>
<div class="row form-group">
<label class="col-xs-3">Event Logging<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="Enabling logging of all events to the device's storage"></i></label>
data-content="Enabling logging of all events to a remote SysLog"></i></label>
<div class="col-xs-9">
<form>
<label class="radio-inline">
@@ -128,6 +126,16 @@
<input type="radio" value="0" name="logeventsenabled" checked>Disabled</label>
</form>
</div>
</div>
<div class="row form-group">
<label class="col-xs-3">Event Log Server IP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="IP address of the SysLog server running on UDP port 514"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" value="" style="display:inline;max-width:185px" id="log_ip"
type="text">
</span>
</div>
<br>
<br>
<div class="col-xs-9 col-md-8">
@@ -142,32 +150,6 @@
<br>
</div>
<div id="eventcontent">
<div class="text-center" id="loading-img">
<h5>Loading Logs. Please wait...<span id="loadpages"></span></h5>
<br>
</div>
<div>
<br>
<legend>Event Log</legend>
<h6 class="text-muted">Dates shown in () represent elapsed time in seconds when NTP Time is disabled</h6>
<div id="logevents" class="label label-danger">Event Logging has been disabled. See Settings->General Settings.
</div>
<br>
<div class="panel panel-default">
<div>
<table id="eventtable" class="table" data-paging="true" data-filtering="true" data-sorting="true"
data-editing="false" data-state="true"></table>
</div>
</div>
<button onclick="clearevent()" class="btn btn-primary btn-sm">Clear Log</button>
<div style="clear:both;">
<br>
<br>
</div>
</div>
</div>
<div id="mqttcontent">
<br>
<legend>MQTT Settings</legend>
@@ -308,10 +290,10 @@
</form>
</div>
</div>
<div class="row form-group" style="display:none" id="hidessid">
<div class="row form-group" style="display:none" id="hideclient">
<label class="col-xs-3">SSID<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="wifi Network Name"></i></label>
data-content="WiFi Network Name"></i></label>
<span class="col-xs-7 col-md-5">
<input class="form-control input-sm" id="inputtohide" type="text" name="ap_ssid">
<select class="form-control input-sm" style="display:none;" id="ssid" onchange="listBSSID();"></select>
@@ -320,16 +302,52 @@
<button id="scanb" type="button" class="btn btn-info btn-xs" style="display:none;"
onclick="scanWifi()">Scan...</button>
</span>
</div>
<div class="row form-group" style="display:none" id="hidepasswd">
<br><br>
<label class="col-xs-3">Password<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="wifi Password"></i></label>
data-content="WiFi Password"></i></label>
<span class="col-xs-9 col-md-5">
<input id="wifipass" class="form-control input-sm" name="ap_passwd" type="password">
</span>
</div>
<br><br>
<h6 style="margin-left: 10px;" class="text-muted">If you're not using DHCP and want a fixed IP address enter the
values below:</h6>
<br>
<label class="col-xs-3">Static IP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="static IP address"></i></label>
<span class="col-xs-9 col-md-5">
<input type="text" class="form-control input-sm" id="staticip" placeholder="">
</span>
<br><br>
<label class="col-xs-3">Gateway IP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="gateway IP address"></i></label>
<span class="col-xs-9 col-md-5">
<input type="text" class="form-control input-sm" id="gatewayip" placeholder="">
</span>
<br><br>
<label class="col-xs-3">Network Mask<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="network mask"></i></label>
<span class="col-xs-9 col-md-5">
<input type="text" class="form-control input-sm" id="nmask" placeholder="255.255.255.0">
</span>
<br><br>
<label class="col-xs-3">DNS IP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="DNS IP address"></i></label>
<span class="col-xs-9 col-md-5">
<input type="text" class="form-control input-sm" id="dnsip" placeholder="">
</span>
</div>
<br><br>
<div class="col-xs-9 col-md-8">
<button onclick="savenetwork()" class="btn btn-primary btn-sm pull-right">Save</button>
</div>
@@ -340,27 +358,17 @@
<legend>Time Settings</legend>
<h6 class="text-muted">With Network Time Protocol (NTP) enabled, all times are adjusted to the local timezone
and
respect daylight saving
time (DST)</h6>
respect daylight saving time (DST)</h6>
<br>
<div class="row form-group">
<label class="col-xs-3">Device Time</label>
<span id="utc" class="col-xs-9 col-md-5">
</span>
</div>
<div class="row form-group">
<label class="col-xs-3">Browser Time</label>
<span id="rtc" class="col-xs-9 col-md-5">
</span>
</div>
<div class="row form-group">
<span id="utc" class="col-xs-9 col-md-5"></span>
<div class="col-xs-3">
<button onclick="syncBrowserTime()" class="btn btn-info btn-sm">Sync Browser Time to Device</button><i
style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" aria-hidden="true"
data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="Use your browser time. Useful when the system does not have an internet connection."></i>
<button onclick="forcentp()" id="forcentp" class="btn btn-info btn-sm">Sync Device with Internet
Time</button>
</div>
</div>
<div class="row form-group">
<label class="col-xs-3">NTP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
@@ -372,8 +380,6 @@
<label class="radio-inline">
<input type="radio" value="0" onclick="handleNTPOFF();" name="ntpenabled" checked>Disabled</label>
</form>
<button onclick="forcentp()" id="forcentp" class="btn btn-info btn-sm">Sync Device with Internet
Time</button>
</div>
</div>
<div class="row form-group">
@@ -391,10 +397,30 @@
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="Time in minutes between scheduled NTP refreshes"></i></label>
<span class="col-xs-9 col-md-5">
<input class="form-control input-sm" placeholder="in Minutes" value="30" id="intervals" type="text">
<input class="form-control input-sm" placeholder="in Minutes" value="30" id="interval" type="text">
</span>
<br>
</div>
<div class="row form-group">
<label class="col-xs-3">Time Zone</label>
<span class="col-xs-9 col-md-5">
<select class="form-control input-sm" name="timzeone" id="timezone">
<option value="0">Australia Eastern (Sydney, Melbourne)</option>
<option value="1">Moscow (MSK, does not observe DST)</option>
<option selected="selected" value="2">Central European Time (Frankfurt, Paris, Amsterdam)</option>
<option value="3">United Kingdom (London, Belfast)</option>
<option value="4">UTC</option>
<option value="5">US Eastern (New York, Detroit)</option>
<option value="6">US Central (Chicago, Houston)</option>
<option value="7">US Mountain (Denver, Salt Lake City)</option>
<option value="8">Arizona (no DST)</option>
<option value="9">US Pacific (Las Vegas, Los Angeles)</option>
<option value="10">Turkey</option>
</select>
</span>
</div>
<br>
<div class="col-xs-9 col-md-8">
<button onclick="saventp()" class="btn btn-primary btn-sm pull-right">Save</button>
@@ -477,26 +503,22 @@
</tr>
</table>
</div>
<div class="panel panel-default table-responsive">
<table class="table">
<caption>MQTT</caption>
<tr>
<td>
<div id="mqttconnected"></div>
</td>
<td>
<div id="mqttheartbeat"></div>
</td>
</tr>
</table>
<div class="panel-heading">
<div class="panel-title" id="mqttloghdr"></div>
</div>
<div>
<table id="mqttlogtable" class="table" data-paging="false" data-filtering="false"
data-sorting="false" data-editing="false" data-state="true"></table>
</div>
<div class="panel panel-default table-responsive">
<table class="table table-hover table-striped table-condensed" border=1>
<caption>MQTT&nbsp;&nbsp;<div id="mqttconnected"></div>&nbsp;<div id="mqttheartbeat"></div>
</caption>
<thead>
<tr>
<th>Time</th>
<th>Topic<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
id="mqttloghdr"></i></th>
<th>Payload</th>
</tr>
</thead>
<tbody id="mqttlog"></tbody>
</table>
</div>
</div>
</div>

View File

@@ -2,78 +2,35 @@ var version = "";
var websock = null;
var wsUri = "ws://" + window.location.host + "/ws";
var utcSeconds;
var data = [];
var ntpSeconds;
var ajaxobj;
var custom_config = {};
var config = {
"command": "configfile",
"network": {
"ssid": "",
"wmode": 1,
"password": ""
},
"general": {
"hostname": "",
"serial": false,
"password": "admin",
"log_events": true
},
"mqtt": {
"enabled": false,
"ip": "",
"port": 1883,
"qos": 1,
"keepalive": 60,
"retain": true,
"base": "",
"user": "",
"password": "",
"heartbeat": false
},
"ntp": {
"server": "pool.ntp.org",
"interval": 30,
"enabled": true
}
};
var xDown = null;
var yDown = null;
var page = 1;
var haspages;
var file = {};
var backupstarted = false;
var updateurl = "";
var updateurl_dev = "";
var use_beta_firmware = false;
var myespcontent;
function browserTime() {
var d = new Date(0);
var c = new Date();
var timestamp = Math.floor((c.getTime() / 1000) + ((c.getTimezoneOffset() * 60) * -1));
d.setUTCSeconds(timestamp);
document.getElementById("rtc").innerHTML = d.toUTCString().slice(0, -3);
}
var formData = new FormData();
var nextIsNotJson = false;
var config = {};
function deviceTime() {
var c = new Date();
var t = new Date(0); // The 0 there is the key, which sets the date to the epoch
var devTime = Math.floor(utcSeconds + ((c.getTimezoneOffset() * 60) * -1));
t.setUTCSeconds(devTime);
t.setUTCSeconds(ntpSeconds);
document.getElementById("utc").innerHTML = t.toUTCString().slice(0, -3);
}
function syncBrowserTime() {
var d = new Date();
var timestamp = Math.floor((d.getTime() / 1000));
var datatosend = {};
datatosend.command = "settime";
datatosend.epoch = timestamp;
websock.send(JSON.stringify(datatosend));
$("#ntp").click();
}
function handleNTPON() {
document.getElementById("forcentp").style.display = "block";
}
@@ -86,7 +43,8 @@ function listntp() {
websock.send("{\"command\":\"gettime\"}");
document.getElementById("ntpserver").value = config.ntp.server;
document.getElementById("intervals").value = config.ntp.interval;
document.getElementById("interval").value = config.ntp.interval;
document.getElementById("timezone").value = config.ntp.timezone;
if (config.ntp.enabled) {
$("input[name=\"ntpenabled\"][value=\"1\"]").prop("checked", true);
@@ -127,7 +85,8 @@ function custom_saveconfig() {
function saventp() {
config.ntp.server = document.getElementById("ntpserver").value;
config.ntp.interval = parseInt(document.getElementById("intervals").value);
config.ntp.interval = parseInt(document.getElementById("interval").value);
config.ntp.timezone = parseInt(document.getElementById("timezone").value);
config.ntp.enabled = false;
if (parseInt($("input[name=\"ntpenabled\"]:checked").val()) === 1) {
@@ -160,6 +119,8 @@ function savegeneral() {
config.general.log_events = true;
}
config.general.log_ip = document.getElementById("log_ip").value;
saveconfig();
}
@@ -205,12 +166,14 @@ function savenetwork() {
config.network.wmode = wmode;
config.network.password = document.getElementById("wifipass").value;
config.network.staticip = document.getElementById("staticip").value;
config.network.gatewayip = document.getElementById("gatewayip").value;
config.network.nmask = document.getElementById("nmask").value;
config.network.dnsip = document.getElementById("dnsip").value;
saveconfig();
}
var formData = new FormData();
function inProgress(callback) {
$("body").load("myesp.html #progresscontent", function (responseTxt, statusTxt, xhr) {
if (statusTxt === "success") {
@@ -283,15 +246,13 @@ function inProgressUpload() {
function handleSTA() {
document.getElementById("scanb").style.display = "block";
document.getElementById("hidessid").style.display = "block";
document.getElementById("hidepasswd").style.display = "block";
document.getElementById("hideclient").style.display = "block";
}
function handleAP() {
document.getElementById("ssid").style.display = "none";
document.getElementById("scanb").style.display = "none";
document.getElementById("hidessid").style.display = "none";
document.getElementById("hidepasswd").style.display = "none";
document.getElementById("hideclient").style.display = "none";
document.getElementById("inputtohide").style.display = "block";
}
@@ -307,6 +268,11 @@ function listnetwork() {
handleSTA();
}
document.getElementById("staticip").value = config.network.staticip;
document.getElementById("gatewayip").value = config.network.gatewayip;
document.getElementById("nmask").value = config.network.nmask;
document.getElementById("dnsip").value = config.network.dnsip;
}
function listgeneral() {
@@ -320,6 +286,9 @@ function listgeneral() {
if (config.general.log_events) {
$("input[name=\"logeventsenabled\"][value=\"1\"]").prop("checked", true);
}
document.getElementById("log_ip").value = config.general.log_ip;
}
function listmqtt() {
@@ -375,32 +344,10 @@ function scanWifi() {
}
}
function getEvents() {
websock.send("{\"command\":\"geteventlog\", \"page\":" + page + "}");
}
function isVisible(e) {
return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
}
function getnextpage(mode) {
if (!backupstarted) {
document.getElementById("loadpages").innerHTML = "Loading " + page + "/" + haspages;
}
if (page < haspages) {
page = page + 1;
var commandtosend = {};
commandtosend.command = mode;
commandtosend.page = page;
websock.send(JSON.stringify(commandtosend));
}
}
function builddata(obj) {
data = data.concat(obj.list);
}
function colorStatusbar(ref) {
var percentage = ref.style.width.slice(0, -1);
if (percentage > 50) { ref.className = "progress-bar progress-bar-success"; } else if (percentage > 25) { ref.className = "progress-bar progress-bar-warning"; } else { ref.class = "progress-bar progress-bar-danger"; }
@@ -444,8 +391,40 @@ function listStats() {
document.getElementById("mqttheartbeat").className = "label label-primary";
}
document.getElementById("mqttloghdr").innerHTML = "MQTT Publish Log: (topics are prefixed with <b>" + ajaxobj.mqttloghdr + "</b>)";
document.getElementById("mqttloghdr").setAttribute('data-content', "Topics are prefixed with " + ajaxobj.mqttloghdr);
var mtable = document.getElementById("mqttlog");
var obj = ajaxobj.mqttlog;
var tr, td;
for (var i = 0; i < obj.length; i++) {
tr = document.createElement("tr");
td = document.createElement("td");
if (obj[i].time < 1563300000) {
td.innerHTML = "(" + obj[i].time + ")";
} else {
var vuepoch = new Date(obj[i].time * 1000);
td.innerHTML = vuepoch.getUTCFullYear() +
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
"-" + twoDigits(vuepoch.getUTCDate()) +
" " + twoDigits(vuepoch.getUTCHours()) +
":" + twoDigits(vuepoch.getUTCMinutes()) +
":" + twoDigits(vuepoch.getUTCSeconds());
}
tr.appendChild(td);
td = document.createElement("td");
td.innerHTML = obj[i].topic
tr.appendChild(td);
td = document.createElement("td");
td.innerHTML = obj[i].payload
tr.appendChild(td);
mtable.appendChild(tr);
}
}
function getContent(contentname) {
@@ -471,19 +450,6 @@ function getContent(contentname) {
case "#networkcontent":
listnetwork();
break;
case "#eventcontent":
page = 1;
data = [];
getEvents();
if (config.general.log_events) {
document.getElementById("logevents").style.display = "none";
} else {
document.getElementById("logevents").style.display = "block";
}
break;
case "#customcontent":
listcustom();
break;
@@ -510,6 +476,7 @@ function getContent(contentname) {
$("#appurl2").text(ajaxobj.appurl);
updateurl = ajaxobj.updateurl;
updateurl_dev = ajaxobj.updateurl_dev;
listCustomStats();
break;
default:
@@ -602,147 +569,12 @@ function twoDigits(value) {
return value;
}
function initEventTable() {
var newlist = [];
for (var i = 0; i < data.length; i++) {
newlist[i] = {};
newlist[i].options = {};
newlist[i].value = {};
try {
var dup = JSON.parse(data[i]);
dup.uid = i + 1;
} catch (e) {
var dup = { "uid": i + 1, "type": "ERRO", "src": "SYS", "desc": "Error in log file", "data": data[i], "time": 1 }
}
newlist[i].value = dup;
var c = dup.type;
switch (c) {
case "WARN":
newlist[i].options.classes = "warning";
break;
case "INFO":
newlist[i].options.classes = "info";
break;
case "ERRO":
newlist[i].options.classes = "danger";
break;
default:
break;
}
}
jQuery(function ($) {
window.FooTable.init("#eventtable", {
columns: [{
"name": "uid",
"title": "ID",
"type": "text",
"sorted": true,
"direction": "DESC"
},
{
"name": "type",
"title": "Event Type",
"type": "text"
},
{
"name": "src",
"title": "Source"
},
{
"name": "desc",
"title": "Description"
},
{
"name": "data",
"title": "Additional Data",
"breakpoints": "xs sm"
},
{
"name": "time",
"title": "Date/Time",
"parser": function (value) {
if (value < 1563300000) {
return "(" + value + ")";
} else {
var comp = new Date();
value = Math.floor(value + ((comp.getTimezoneOffset() * 60) * -1));
var vuepoch = new Date(value * 1000);
var formatted = vuepoch.getUTCFullYear() +
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
"-" + twoDigits(vuepoch.getUTCDate()) +
" " + twoDigits(vuepoch.getUTCHours()) +
":" + twoDigits(vuepoch.getUTCMinutes()) +
":" + twoDigits(vuepoch.getUTCSeconds());
return formatted;
}
},
"breakpoints": "xs sm"
}
],
rows: newlist
});
});
}
function initMQTTLogTable() {
var newlist = [];
for (var i = 0; i < ajaxobj.mqttlog.length; i++) {
var data = JSON.stringify(ajaxobj.mqttlog[i]);
newlist[i] = {};
newlist[i].options = {};
newlist[i].value = {};
newlist[i].value = JSON.parse(data);
newlist[i].options.classes = "warning";
newlist[i].options.style = "color: blue";
}
jQuery(function ($) {
window.FooTable.init("#mqttlogtable", {
columns: [{
"name": "time",
"title": "Last Published",
"style": { "min-width": "160px" },
"parser": function (value) {
if (value < 1563300000) {
return "(" + value + ")";
} else {
var comp = new Date();
value = Math.floor(value + ((comp.getTimezoneOffset() * 60) * -1));
var vuepoch = new Date(value * 1000);
var formatted = vuepoch.getUTCFullYear() +
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
"-" + twoDigits(vuepoch.getUTCDate()) +
" " + twoDigits(vuepoch.getUTCHours()) +
":" + twoDigits(vuepoch.getUTCMinutes()) +
":" + twoDigits(vuepoch.getUTCSeconds());
return formatted;
}
},
"breakpoints": "xs sm"
},
{
"name": "topic",
"title": "Topic",
},
{
"name": "payload",
"title": "Payload",
},
],
rows: newlist
});
});
}
var nextIsNotJson = false;
function socketMessageListener(evt) {
var obj = JSON.parse(evt.data);
if (obj.hasOwnProperty("command")) {
switch (obj.command) {
case "status":
ajaxobj = obj;
initMQTTLogTable();
getContent("#statuscontent");
break;
case "custom_settings":
@@ -752,17 +584,8 @@ function socketMessageListener(evt) {
ajaxobj = obj;
getContent("#custom_statuscontent");
break;
case "eventlist":
haspages = obj.haspages;
if (haspages === 0) {
document.getElementById("loading-img").style.display = "none";
initEventTable();
break;
}
builddata(obj);
break;
case "gettime":
utcSeconds = obj.epoch;
ntpSeconds = obj.epoch;
deviceTime();
break;
case "ssidlist":
@@ -778,26 +601,6 @@ function socketMessageListener(evt) {
break;
}
}
if (obj.hasOwnProperty("resultof")) {
switch (obj.resultof) {
case "eventlist":
if (page < haspages && obj.result === true) {
getnextpage("geteventlog");
} else if (page === haspages) {
initEventTable();
document.getElementById("loading-img").style.display = "none";
}
break;
default:
break;
}
}
}
function clearevent() {
websock.send("{\"command\":\"clearevent\"}");
$("#eventlog").click();
}
function compareDestroy() {
@@ -814,46 +617,6 @@ function restart() {
inProgress("restart");
}
$("#dismiss, .overlay").on("click", function () {
$("#sidebar").removeClass("active");
$(".overlay").fadeOut();
});
$("#sidebarCollapse").on("click", function () {
$("#sidebar").addClass("active");
$(".overlay").fadeIn();
$(".collapse.in").toggleClass("in");
$("a[aria-expanded=true]").attr("aria-expanded", "false");
});
$("#custom_status").click(function () {
websock.send("{\"command\":\"custom_status\"}");
return false;
});
$("#status").click(function () {
websock.send("{\"command\":\"status\"}");
return false;
});
$("#custom").click(function () { getContent("#customcontent"); return false; });
$("#network").on("click", (function () { getContent("#networkcontent"); return false; }));
$("#general").click(function () { getContent("#generalcontent"); return false; });
$("#mqtt").click(function () { getContent("#mqttcontent"); return false; });
$("#ntp").click(function () { getContent("#ntpcontent"); return false; });
$("#backup").click(function () { getContent("#backupcontent"); return false; });
$("#reset").click(function () { $("#destroy").modal("show"); return false; });
$("#restart").click(function () { $("#reboot").modal("show"); return false; });
$("#eventlog").click(function () { getContent("#eventcontent"); return false; });
$(".noimp").on("click", function () {
$("#noimp").modal("show");
});
var xDown = null;
var yDown = null;
function handleTouchStart(evt) {
xDown = evt.touches[0].clientX;
yDown = evt.touches[0].clientY;
@@ -953,8 +716,26 @@ function login() {
}
}
function getfirmware() {
if (use_beta_firmware) {
use_beta_firmware = false;
document.getElementById("updateb").innerHTML = "Switch to Development build";
} else {
use_beta_firmware = true;
document.getElementById("updateb").innerHTML = "Switch to Stable release";
}
getLatestReleaseInfo();
}
function getLatestReleaseInfo() {
$.getJSON(updateurl).done(function (release) {
if (use_beta_firmware) {
var url = updateurl_dev;
} else {
var url = updateurl;
}
$.getJSON(url).done(function (release) {
var asset = release.assets[0];
var downloadCount = 0;
for (var i = 0; i < release.assets.length; i++) {
@@ -978,10 +759,6 @@ function getLatestReleaseInfo() {
}).error(function () { $("#onlineupdate").html("<h5>Couldn't get release details. Make sure there is an Internet connection.</h5>"); });
}
$("#update").on("shown.bs.modal", function (e) {
getLatestReleaseInfo();
});
function allowUpload() {
$("#upbtn").prop("disabled", false);
}
@@ -1006,7 +783,7 @@ function start() {
});
}
function refreshEMS() {
function refreshCustomStatus() {
websock.send("{\"command\":\"custom_status\"}");
}
@@ -1014,5 +791,30 @@ function refreshStatus() {
websock.send("{\"command\":\"status\"}");
}
$("#dismiss, .overlay").on("click", function () {
$("#sidebar").removeClass("active");
$(".overlay").fadeOut();
});
$("#sidebarCollapse").on("click", function () {
$("#sidebar").addClass("active");
$(".overlay").fadeIn();
$(".collapse.in").toggleClass("in");
$("a[aria-expanded=true]").attr("aria-expanded", "false");
});
$("#custom_status").click(function () { websock.send("{\"command\":\"custom_status\"}"); return false; });
$("#status").click(function () { websock.send("{\"command\":\"status\"}"); return false; });
$("#custom").click(function () { getContent("#customcontent"); return false; });
$("#network").on("click", (function () { getContent("#networkcontent"); return false; }));
$("#general").click(function () { getContent("#generalcontent"); return false; });
$("#mqtt").click(function () { getContent("#mqttcontent"); return false; });
$("#ntp").click(function () { getContent("#ntpcontent"); return false; });
$("#backup").click(function () { getContent("#backupcontent"); return false; });
$("#reset").click(function () { $("#destroy").modal("show"); return false; });
$("#restart").click(function () { $("#reboot").modal("show"); return false; });
$(".noimp").on("click", function () { $("#noimp").modal("show"); });
$("#update").on("shown.bs.modal", function (e) { getfirmware(); });
document.addEventListener("touchstart", handleTouchStart, false);
document.addEventListener("touchmove", handleTouchMove, false);

View File

@@ -100,7 +100,7 @@ gulp.task('myespjs', 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'])
return gulp.src(['../../src/websrc/3rdparty/js/jquery-1.12.4.min.js', '../../src/websrc/3rdparty/js/bootstrap-3.4.1.min.js'])
.pipe(concat({
path: 'required.js',
stat: {
@@ -117,7 +117,7 @@ gulp.task('requiredjs', 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'])
return gulp.src(['../../src/websrc/3rdparty/css/bootstrap-3.4.1.min.css', '../../src/websrc/3rdparty/css/sidebar.css'])
.pipe(concat({
path: 'required.css',
stat: {

0
tools/wsemulator/run.ps1 Normal file → Executable file
View File

0
tools/wsemulator/run.sh Normal file → Executable file
View File

View File

@@ -47,30 +47,25 @@ var networks = {
]
}
var eventlog = {
"command": "eventlist",
"page": 1,
"haspages": 1,
"list": [
"{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #1\",\"time\": 1563371160}",
"{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #2\",\"time\":0}",
"{\"type\":\"INFO\",\"src\":\"system\",\"desc\":\"System booted\",\"data\":\"\",\"time\":1568660479}",
"{\"type\":\"WARN\",\"src\":\"system\",\"desc\":\"test data\",\"data\":\"Record #3\",\"time\":0}"
]
}
var configfile = {
"command": "configfile",
"network": {
"ssid": "myssid",
"wmode": 0,
"password": "password"
"password": "password",
"password": "",
"staticip": "",
"gatewayip": "",
"nmask": "",
"dnsip": ""
},
"general": {
"hostname": "myesp",
"hostname": "ems-esp",
"password": "admin",
"serial": true,
"log_events": true
"version": "1.0.0",
"log_events": false,
"log_ip": "10.11.12.13"
},
"mqtt": {
"enabled": false,
@@ -86,8 +81,9 @@ var configfile = {
},
"ntp": {
"server": "pool.ntp.org",
"interval": "30",
"enabled": false
"interval": 720,
"timezone": 2,
"enabled": true
}
};
@@ -101,21 +97,11 @@ var custom_configfile = {
"listen_mode": false,
"shower_timer": true,
"shower_alert": false,
"publish_time": 120,
"publish_time": 0,
"tx_mode": 1
}
};
function sendEventLog() {
wss.broadcast(eventlog);
var res = {
"command": "result",
"resultof": "eventlist",
"result": true
};
wss.broadcast(res);
}
function sendStatus() {
var stats = {
"command": "status",
@@ -127,7 +113,7 @@ function sendStatus() {
"availsize": 2469,
"ip": "10.10.10.198",
"ssid": "my_ssid",
"mac": "DC:4F:11:22:93:06",
"mac": "DC:4F:12:22:13:06",
"signalstr": 62,
"systemload": 0,
"mqttconnected": true,
@@ -155,6 +141,7 @@ function sendCustomStatus() {
"customname": "EMS-ESP",
"appurl": "https://github.com/proddy/EMS-ESP",
"updateurl": "https://api.github.com/repos/proddy/EMS-ESP/releases/latest",
"updateurl_dev": "https://api.github.com/repos/proddy/EMS-ESP/releases/tags/travis-dev-build",
"emsbus": {
"ok": true,
@@ -241,28 +228,13 @@ wss.on('connection', function connection(ws) {
console.log("[INFO] Sending time");
var res = {};
res.command = "gettime";
res.epoch = Math.floor((new Date).getTime() / 1000);
//res.epoch = 1567107755;
wss.broadcast(res);
break;
case "settime":
console.log("[INFO] Setting time (fake)");
var res = {};
res.command = "gettime";
res.epoch = Math.floor((new Date).getTime() / 1000);
res.epoch = 1572613374; // this is 13:02:54 CET
wss.broadcast(res);
break;
case "getconf":
console.log("[INFO] Sending system configuration file (if set any)");
wss.broadcast(configfile);
break;
case "geteventlog":
console.log("[INFO] Sending eventlog");
sendEventLog();
break;
case "clearevent":
console.log("[INFO] Clearing eventlog");
break;
case "restart":
console.log("[INFO] Restart");
break;