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 .vscode
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/*
# platformio # platformio
.pio .pio
.clang_complete
.gcc-flags.json
lib/readme.txt lib/readme.txt
# web stuff compiled # web stuff compiled
src/websrc/css/required.css
src/websrc/js/required.js
src/websrc/fonts
src/websrc/gzipped
src/websrc/temp src/websrc/temp
*.gz.h src/webh/*.gz.h
# NPM directories # NPM directories
node_modules/ node_modules
jspm_packages/
.npm
# Output of 'npm pack' # OS specific
*.tgz .DS_Store
# dotenv environment variables file
.env
# project specfic # project specfic
.DS_Store
scripts/stackdmp.txt 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/), 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). 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 ## [1.9.3] 2019-10-26
### Added ### Added
- Report # TCP dropouts in the `system` command. These could be due to WiFI or MQTT disconnected. - 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 - Added temp and mode to the MQTT `thermostat_cmd` topic
- build scripts for automated CI with TravisCI
### Fixed ### 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) - publish dallas external temp sensors to MQTT (thanks @JewelZB)
- shower timer and shower alert options available via set commands - 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 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) - 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 - `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 ### Changed
- Renamed project from EMS-ESP-Boiler to EMS-ESP since it's kinda EMS generic now - 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` - 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 - 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) - Scanning known EMS Devices now ignores duplicates (https://github.com/proddy/EMS-ESP/pull/30)
- ServiceCode stored as a two byte char - 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 ## [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) [![release-date](https://img.shields.io/github/release-date/proddy/EMS-ESP.svg?label=Released)](https://github.com/proddy/EMS-ESP/commits/master)
<br /> <br />
[![license](https://img.shields.io/github/license/proddy/EMS-ESP.svg)](LICENSE) [![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) [![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) [![downloads](https://img.shields.io/github/downloads/proddy/EMS-ESP/total.svg)](https://github.com/proddy/EMS-ESP/releases)
<br /> <br />

View File

@@ -4,6 +4,7 @@
- "auto" - "auto"
- "heat" - "heat"
- "off" - "off"
mode_command_topic: "home/ems-esp/thermostat_cmd_mode1" mode_command_topic: "home/ems-esp/thermostat_cmd_mode1"
temperature_command_topic: "home/ems-esp/thermostat_cmd_temp1" temperature_command_topic: "home/ems-esp/thermostat_cmd_temp1"
@@ -11,7 +12,8 @@
current_temperature_topic: "home/ems-esp/thermostat_data" current_temperature_topic: "home/ems-esp/thermostat_data"
temperature_state_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 }}" current_temperature_template: "{{ value_json.hc1.currtemp }}"
temperature_state_template: "{{ value_json.hc1.seltemp }}" temperature_state_template: "{{ value_json.hc1.seltemp }}"

View File

@@ -20,16 +20,6 @@
# boiler # 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 - platform: mqtt
state_topic: 'home/ems-esp/boiler_data' state_topic: 'home/ems-esp/boiler_data'
name: 'Warm Water selected temperature' name: 'Warm Water selected temperature'

View File

@@ -1,20 +1,26 @@
title: Home - id: ems-esp_id0
views: title: Heating
- title: Heating
cards: 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 title: Boiler
show_header_toggle: false show_header_toggle: false
entities: entities:
- sensor.boiler_boottime - sensor.boiler_boottime
- sensor.boiler_updated - sensor.boiler_updated
- sensor.ems_esp_status
- type: divider - type: divider
- sensor.warm_water_selected_temperature - sensor.warm_water_selected_temperature
- sensor.warm_water_current_temperature - sensor.warm_water_current_temperature
- sensor.warm_water_activated - sensor.warm_water_activated
- sensor.warm_water_3_way_valve - sensor.warm_water_3_way_valve
- sensor.warm_water_tapwater_flow_rate
- type: divider - type: divider
- sensor.boiler_temperature - sensor.boiler_temperature
- sensor.return_temperature - sensor.return_temperature
@@ -28,7 +34,8 @@ views:
- sensor.burner_max_power - sensor.burner_max_power
- sensor.burner_current_power - sensor.burner_current_power
- type: vertical-stack - id: ems-esp_id3
type: vertical-stack
cards: cards:
- type: entities - type: entities
title: Shower Monitor title: Shower Monitor
@@ -49,14 +56,20 @@ views:
service_data: service_data:
entity_id: script.shower_coldshot 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: cards:
- type: history-graph - type: history-graph
entities: entities:
- sensor.pc_room_sensor_temperature
- sensor.current_room_temperature - sensor.current_room_temperature
- sensor.dark_sky_temperature - sensor.dark_sky_temperature
- type: thermostat - type: thermostat
entity: climate.thermostat entity: climate.thermostat
- type: thermostat - type: thermostat
name: WarmWater
entity: climate.boiler 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] [platformio]
default_envs = debug default_envs = release
;default_envs = debug
[common] [common]
; custom build options are: ; custom build options are:
@@ -11,25 +12,48 @@ default_envs = debug
; -DTESTS ; -DTESTS
; -DCRASH ; -DCRASH
; -DFORCE_SERIAL ; -DFORCE_SERIAL
; -DMYESP_DEBUG
;custom_flags = -DFORCE_SERIAL -DMYESP_DEBUG
custom_flags = 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 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] [env]
framework = arduino framework = arduino
;platform = espressif8266@2.2.2 ; arduino core 2.5.2
platform = espressif8266 platform = espressif8266
;platform = https://github.com/platformio/platform-espressif8266#develop
;platform = https://github.com/platformio/platform-espressif8266#feature/stage
lib_deps = lib_deps =
https://github.com/bakercp/CRC32
https://github.com/rlogiacco/CircularBuffer https://github.com/rlogiacco/CircularBuffer
https://github.com/PaulStoffregen/OneWire https://github.com/PaulStoffregen/OneWire
https://github.com/xoseperez/justwifi https://github.com/me-no-dev/ESPAsyncWebServer
https://github.com/marvinroger/async-mqtt-client
https://github.com/xoseperez/eeprom_rotate
https://github.com/bblanchon/ArduinoJson
https://github.com/me-no-dev/ESPAsyncUDP https://github.com/me-no-dev/ESPAsyncUDP
https://github.com/me-no-dev/ESPAsyncTCP uuid-common@^1.1.0
https://github.com/me-no-dev/ESPAsyncWebServer#b0c6144 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 upload_speed = 921600
monitor_speed = 115200 monitor_speed = 115200
@@ -37,35 +61,54 @@ monitor_speed = 115200
;upload_port = /dev/cu.wchusbserial14403 ;upload_port = /dev/cu.wchusbserial14403
;upload_port = /dev/cu.usbserial-1440 ;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_protocol = espota
upload_port = ems-esp.local 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] [env:travis]
board = esp12e board = esp12e
build_flags = ${common.general_flags} build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:esp12e] [env:esp12e]
board = esp12e board = esp12e
build_flags = ${common.general_flags} build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:d1_mini] [env:d1_mini]
board = d1_mini board = d1_mini
build_flags = ${common.general_flags} build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:nodemcuv2] [env:nodemcuv2]
board = nodemcuv2 board = nodemcuv2
build_flags = ${common.general_flags} build_flags = ${common.build_flags}
extra_scripts = scripts/main_script.py
[env:nodemcu] [env:nodemcu]
board = 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] [env:debug]
board = d1_mini board = d1_mini
build_type = debug build_type = debug
build_flags = ${common.general_flags} ${common.custom_flags} build_flags = ${common.build_flags} ${common.custom_flags}
extra_scripts = extra_scripts =
pre:scripts/rename_fw.py pre:scripts/pre_script.py
pre:scripts/buildweb.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() { stat_bytes() {
echo "size is:" filesize=`du -k "$1" | cut -f1;`
case "$(uname -s)" in echo 'size:' $filesize 'bytes'
Darwin) stat -f %z "$1";;
*) stat -c %s "$1";;
esac
} }
# Available environments # Available environments
list_envs() { list_envs() {
grep env: ../platformio.ini | sed 's/\[env:\(.*\)\]/\1/g' grep env: platformio.ini | sed 's/\[env:\(.*\)\]/\1/g'
} }
print_available() { print_available() {
@@ -59,7 +56,7 @@ set_default_environments() {
} }
build_webui() { build_webui() {
cd ../tools/webfilesbuilder cd ./tools/webfilesbuilder
# Build system uses gulpscript.js to build web interface # Build system uses gulpscript.js to build web interface
if [ ! -e node_modules/gulp/bin/gulp.js ]; then if [ ! -e node_modules/gulp/bin/gulp.js ]; then
@@ -73,26 +70,22 @@ build_webui() {
echo "Building web interface..." echo "Building web interface..."
node node_modules/gulp/bin/gulp.js || exit 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 ../.. cd ../..
} }
build_environments() { build_environments() {
echo "--------------------------------------------------------------" echo "--------------------------------------------------------------"
echo "Building firmware images..." 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 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 platformio run --silent --environment $environment || exit 1
stat_bytes .pio/build/$environment/firmware.bin stat_bytes .pio/build/$environment/firmware.bin
[[ "${TRAVIS_BUILD_STAGE_NAME}" = "Test" ]] || \ # mv .pio/build/$environment/firmware.bin $destination/EMS-ESP-$version-$environment.bin
mv .pio/build/$environment/firmware.bin $destination/EMS-ESP-$version/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 done
echo "--------------------------------------------------------------" echo "--------------------------------------------------------------"
} }
@@ -101,7 +94,7 @@ build_environments() {
####### MAIN ####### MAIN
destination=firmware 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') version=$(grep -E '^#define APP_VERSION' $version_file | awk '{print $3}' | sed 's/"//g')
if ${TRAVIS:-false}; then if ${TRAVIS:-false}; then
@@ -115,6 +108,8 @@ else
git_tag= git_tag=
fi fi
echo $git_tag
if [[ -n $git_tag ]]; then if [[ -n $git_tag ]]; then
new_version=${version/-*} new_version=${version/-*}
sed -i -e "s@$version@$new_version@" $version_file sed -i -e "s@$version@$new_version@" $version_file
@@ -139,7 +134,7 @@ fi
travis=$(list_envs | grep travis | sort) travis=$(list_envs | grep travis | sort)
# get all taregts, excluding travis and debug # 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}" export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS}"
@@ -183,7 +178,7 @@ fi
# for debugging # for debugging
echo "* git_revision = $git_revision" echo "* git_revision = $git_revision"
echo "* git_tag = $git_tag" echo "* git_tag = $git_tag"
echo "* TRAVIS_EVENT_TYPE = $TRAVIS_EVENT_TYPE" echo "* TRAVIS_COMMIT = $TRAVIS_COMMIT"
echo "* TRAVIS_TAG = $TRAVIS_TAG" echo "* TRAVIS_TAG = $TRAVIS_TAG"
echo "* TRAVIS_BRANCH = $TRAVIS_BRANCH" echo "* TRAVIS_BRANCH = $TRAVIS_BRANCH"
echo "* TRAVIS_BUILD_STAGE_NAME = $TRAVIS_BUILD_STAGE_NAME" 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 re
Import("env") Import("env")
def build_web(source, target, env): def build_web():
print("\n** Build web...") print("** Building web...")
call(["gulp", "-f", os.getcwd()+"/tools/webfilesbuilder/gulpfile.js"]) 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 = {} bag = {}
exprs = [ exprs = [
(re.compile(r'^#define APP_VERSION\s+"(\S+)"'), 'app_version'), (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_version = bag.get('app_version')
app_name = bag.get('app_name') app_name = bag.get('app_name')
app_hostname = bag.get('app_hostname') app_hostname = bag.get('app_hostname')
board = env['BOARD'] board = env['BOARD']
branch = env['PIOENV'] branch = env['PIOENV']
# build the web files
env.AddPreAction("buildprog", build_web)
# build filename, replacing . with _ for the version # build filename, replacing . with _ for the version
#env.Replace(PROGNAME="firmware_%s" % branch + "_" + app_version.replace(".", "_")) env.Replace(PROGNAME=app_name + "-" +
#env.Replace(PROGNAME=app_name + "-" + app_version.replace(".", "_") + "-" + board + "-" + branch) 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 * Paul Derbyshire - first version December 2018
*/ */
@@ -9,16 +9,23 @@
#ifndef MyESP_h #ifndef MyESP_h
#define MyESP_h #define MyESP_h
#define MYESP_VERSION "1.2.12" #define MYESP_VERSION "1.2.22"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ArduinoOTA.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 <AsyncMqttClient.h>
#include <ESPAsyncUDP.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <FS.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 "Ntp.h"
#include "TelnetSpy.h" // modified from https://github.com/yasheena/telnetspy #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_CONFIG_FILE "/myesp.json"
#define MYESP_CUSTOMCONFIG_FILE "/customconfig.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_USERNAME "admin" // HTTP username
#define MYESP_HTTP_PASSWORD "admin" // default password #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_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 #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 // MQTT
#define MQTT_PORT 1883 // MQTT port #define MQTT_PORT 1883 // MQTT port
#define MQTT_RECONNECT_DELAY_MIN 2000 // Try to reconnect in 3 seconds upon disconnection #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_DISCONNECT_EVENT 1
#define MQTT_MESSAGE_EVENT 2 #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_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_ON '1' // for MQTT switch on
#define MYESP_MQTT_PAYLOAD_OFF '0' // for MQTT switch off #define MYESP_MQTT_PAYLOAD_OFF '0' // for MQTT switch off
@@ -102,6 +115,7 @@ extern struct rst_info resetInfo;
// Telnet // Telnet
#define TELNET_SERIAL_BAUD 115200 #define TELNET_SERIAL_BAUD 115200
#define TELNET_MAX_COMMAND_LENGTH 80 // length of a command #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_CONNECT 1
#define TELNET_EVENT_DISCONNECT 0 #define TELNET_EVENT_DISCONNECT 0
#define TELNET_EVENT_SHOWCMD 10 #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_FACTORY 5 // Factory reset
#define CUSTOM_RESET_MAX 5 #define CUSTOM_RESET_MAX 5
// SPIFFS // SPIFFS - max allocation is 1000 KB
// https://arduinojson.org/v6/assistant/ // https://arduinojson.org/v6/assistant/
#define MYESP_SPIFFS_MAXSIZE_CONFIG 800 // max size for a config file #define MYESP_SPIFFS_MAXSIZE_CONFIG 999 // max size for a config file
#define MYESP_SPIFFS_MAXSIZE_EVENTLOG 20000 // max size for the eventlog in bytes
// CRASH // CRASH
/** /**
@@ -207,14 +220,16 @@ typedef struct {
char description[100]; char description[100];
} command_t; } 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 { typedef enum {
MYESP_BOOTSTATUS_POWERON = 0, MYESP_BOOTSTATUS_POWERON = 0,
MYESP_BOOTSTATUS_BOOTED = 1, MYESP_BOOTSTATUS_BOOTED = 1,
MYESP_BOOTSTATUS_BOOTING = 2, MYESP_BOOTSTATUS_BOOTING = 2,
MYESP_BOOTSTATUS_RESETNEEDED = 3 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 // for storing all MQTT publish messages
typedef struct { typedef struct {
@@ -222,15 +237,15 @@ typedef struct {
char * topic; char * topic;
char * payload; char * payload;
time_t timestamp; 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(unsigned int, const char *, const char *)> mqtt_callback_f;
typedef std::function<void()> wifi_callback_f; typedef std::function<void()> wifi_callback_f;
typedef std::function<void()> ota_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, const char *)> telnetcommand_callback_f;
typedef std::function<void(uint8_t)> telnet_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_t, 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, uint8_t, const char *, const char *)> fs_setlist_callback_f;
typedef std::function<void(JsonObject root)> web_callback_f; typedef std::function<void(JsonObject root)> web_callback_f;
// calculates size of an 2d array at compile time // 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 #define MYESP_UPTIME_OVERFLOW 4294967295 // Uptime overflow value
#define MYESP_MAX_STR_LEN 16 // web min and max length of wifi ssid and password
// web min and max length of wifi ssid and password
#define MYESP_MAX_STR_LEN 16
#define MYESP_BOOTUP_FLASHDELAY 50 // flash duration for LED at bootup sequence #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 #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();
~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 // wifi
void setWIFICallback(void (*callback)()); void setWIFICallback(void (*callback)());
void setWIFI(wifi_callback_f callback); void setWIFI(wifi_callback_f callback);
@@ -288,6 +297,9 @@ class MyESP {
bool getUseSerial(); bool getUseSerial();
void setUseSerial(bool toggle); void setUseSerial(bool toggle);
// syslog
void writeLogEvent(const uint8_t type, const char * msg);
// FS // FS
void setSettings(fs_loadsave_callback_f loadsave, fs_setlist_callback_f setlist, bool useSerial = true); void setSettings(fs_loadsave_callback_f loadsave, fs_setlist_callback_f setlist, bool useSerial = true);
bool fs_saveConfig(JsonObject root); 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(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(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(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); bool fs_setSettingValue(bool * setting, const char * value, bool value_default);
// Web // Web
@@ -308,7 +321,7 @@ class MyESP {
// general // general
void end(); void end();
void loop(); 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(); void resetESP();
int getWifiQuality(); int getWifiQuality();
void showSystemStats(); void showSystemStats();
@@ -317,6 +330,8 @@ class MyESP {
uint32_t getSystemResetReason(); uint32_t getSystemResetReason();
uint8_t getSystemBootStatus(); uint8_t getSystemBootStatus();
bool _have_ntp_time; bool _have_ntp_time;
unsigned long getSystemTime();
void heartbeatPrint();
private: private:
// mqtt // mqtt
@@ -328,10 +343,10 @@ class MyESP {
char * _mqttTopic(const char * topic); char * _mqttTopic(const char * topic);
// mqtt log // 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 _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 AsyncMqttClient mqttClient; // the MQTT class
uint32_t _mqtt_reconnect_delay; uint32_t _mqtt_reconnect_delay;
@@ -359,6 +374,10 @@ class MyESP {
char * _network_ssid; char * _network_ssid;
char * _network_password; char * _network_password;
uint8_t _network_wmode; uint8_t _network_wmode;
char * _network_staticip;
char * _network_gatewayip;
char * _network_nmask;
char * _network_dnsip;
bool _wifi_connected; bool _wifi_connected;
String _getESPhostname(); String _getESPhostname();
@@ -371,7 +390,7 @@ class MyESP {
// crash // crash
void _eeprom_setup(); void _eeprom_setup();
// telnet & debug // telnet
TelnetSpy SerialAndTelnet; TelnetSpy SerialAndTelnet;
void _telnetConnected(); void _telnetConnected();
void _telnetDisconnected(); void _telnetDisconnected();
@@ -385,6 +404,9 @@ class MyESP {
telnet_callback_f _telnet_callback_f; // callback for connect/disconnect telnet_callback_f _telnet_callback_f; // callback for connect/disconnect
bool _changeSetting(uint8_t wc, const char * setting, const char * value); bool _changeSetting(uint8_t wc, const char * setting, const char * value);
// syslog
void _syslog_setup();
// fs and settings // fs and settings
void _fs_setup(); void _fs_setup();
bool _fs_loadConfig(); bool _fs_loadConfig();
@@ -407,9 +429,11 @@ class MyESP {
char * _app_version; char * _app_version;
char * _app_url; char * _app_url;
char * _app_updateurl; char * _app_updateurl;
char * _app_updateurl_dev;
bool _suspendOutput; bool _suspendOutput;
bool _general_serial; bool _general_serial;
bool _general_log_events; bool _general_log_events;
char * _general_log_ip;
char * _buildTime; char * _buildTime;
bool _timerequest; bool _timerequest;
bool _formatreq; bool _formatreq;
@@ -417,6 +441,7 @@ class MyESP {
char * _getBuildTime(); char * _getBuildTime();
bool _hasValue(const char * s); bool _hasValue(const char * s);
void _printHeap(const char * s); void _printHeap(const char * s);
void _kick();
// reset reason and rtcmem // reset reason and rtcmem
bool _rtcmem_status; bool _rtcmem_status;
@@ -455,15 +480,12 @@ class MyESP {
uint32_t _getUsedHeap(); uint32_t _getUsedHeap();
// heartbeat // heartbeat
void _heartbeatCheck(bool force); void _heartbeatCheck(bool force = false);
// web // web
web_callback_f _web_callback_f; web_callback_f _web_callback_f;
const char * _http_username; const char * _http_username;
// log
void _sendEventLog(uint8_t page);
// web // web
void _onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len); void _onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len);
void _procMsg(AsyncWebSocketClient * client, size_t sz); void _procMsg(AsyncWebSocketClient * client, size_t sz);
@@ -472,14 +494,12 @@ class MyESP {
void _printScanResult(int networksFound); void _printScanResult(int networksFound);
void _sendTime(); void _sendTime();
void _webserver_setup(); void _webserver_setup();
void _webRootPage();
void _webResetPage();
void _webResetAllPage();
// ntp // ntp
char * _ntp_server; char * _ntp_server;
uint8_t _ntp_interval; uint16_t _ntp_interval;
bool _ntp_enabled; bool _ntp_enabled;
uint8_t _ntp_timezone;
}; };
extern MyESP myESP; extern MyESP myESP;

View File

@@ -3,16 +3,86 @@
*/ */
#include "Ntp.h" #include "Ntp.h"
#include "MyESP.h"
char * NtpClient::TimeServerName; char * NtpClient::TimeServerName;
Timezone * NtpClient::tz;
TimeChangeRule * NtpClient::tcr;
time_t NtpClient::syncInterval; time_t NtpClient::syncInterval;
IPAddress NtpClient::timeServer; IPAddress NtpClient::timeServer;
AsyncUDP NtpClient::udpListener; AsyncUDP NtpClient::udpListener;
byte NtpClient::NTPpacket[NTP_PACKET_SIZE]; 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); TimeServerName = strdup(server);
syncInterval = syncMins * 60; // convert to seconds 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); WiFi.hostByName(TimeServerName, timeServer);
setSyncProvider(getNtpTime); setSyncProvider(getNtpTime);
setSyncInterval(syncInterval); 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 highWord = word(packet.data()[40], packet.data()[41]);
unsigned long lowWord = word(packet.data()[42], packet.data()[43]); unsigned long lowWord = word(packet.data()[42], packet.data()[43]);
time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL; 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)); udpListener.write(NTPpacket, sizeof(NTPpacket));

View File

@@ -10,18 +10,24 @@
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncUDP.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 { class NtpClient {
public: 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(); ICACHE_FLASH_ATTR virtual ~NtpClient();
static char * TimeServerName; static char * TimeServerName;
static IPAddress timeServer; static IPAddress timeServer;
static time_t syncInterval; static time_t syncInterval;
static Timezone * tz;
static TimeChangeRule * tcr;
static AsyncUDP udpListener; static AsyncUDP udpListener;

View File

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

View File

@@ -151,13 +151,13 @@
#ifndef TelnetSpy_h #ifndef TelnetSpy_h
#define 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_MIN_BLOCK_SIZE 64
#define TELNETSPY_COLLECTING_TIME 100 #define TELNETSPY_COLLECTING_TIME 100
#define TELNETSPY_MAX_BLOCK_SIZE 512 #define TELNETSPY_MAX_BLOCK_SIZE 512
#define TELNETSPY_PING_TIME 1500 #define TELNETSPY_PING_TIME 1500
#define TELNETSPY_PORT 23 #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_WELCOME_MSG "Connection established via Telnet.\n"
#define TELNETSPY_REJECT_MSG "Telnet: Only one connection possible.\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" #include "TimeLib.h"
static tmElements_t tm; // a cache of time elements 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 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) { void refreshCache(time_t t) {
if (t != cacheTime) { if (t != cacheTime) {
breakTime(t, tm); breakTime(t, tm);
@@ -118,6 +151,26 @@ uint8_t to_hour(time_t t) { // the hour for the given time
return tm.Hour; 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) { void setTime(time_t t) {
sysTime = (uint32_t)t; sysTime = (uint32_t)t;
nextSyncTime = (uint32_t)t + syncInterval; 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 #ifndef _TimeLib_h
#define _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_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_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_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 #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"> <div class="row form-group">
<label class="col-xs-3">Publish Time<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" <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" 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"> <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"> id="publish_time" type="text">
</span> </span>
<br> <br>
@@ -131,7 +131,7 @@
<select class="form-control input-sm" id="tx_mode"> <select class="form-control input-sm" id="tx_mode">
<option selected="selected" value="1">1 (EMS generic)</option> <option selected="selected" value="1">1 (EMS generic)</option>
<option value="2">2 (EMS+)</option> <option value="2">2 (EMS+)</option>
<option value="3">3 (Junkers Heatronic)</option> <option value="3">3 (Junkers Heatronic HT3)</option>
</select> </select>
</span> </span>
</div> </div>
@@ -257,6 +257,7 @@
</div> </div>
</div> </div>
<div class="row form-group" style="text-align: center;"> <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> </div>
<br>
</div> </div>

View File

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

View File

@@ -39,7 +39,7 @@ class DS18 {
uint8_t setup(uint8_t gpio, bool parasite); uint8_t setup(uint8_t gpio, bool parasite);
void loop(); void loop();
char * getDeviceString(char * s, unsigned char index); 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 int16_t getRawValue(unsigned char index); // raw values, needs / 16
protected: 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 #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_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. #define EMS_MAX_TELEGRAM_LENGTH 32 // max length of a telegram, including CRC, for Rx and Tx.
// default values for null values // default values for null values
#define EMS_VALUE_INT_ON 1 // boolean true #define EMS_VALUE_BOOL_ON 0x01 // boolean true
#define EMS_VALUE_INT_OFF 0 // boolean false #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_INT_NOTSET 0xFF // for 8-bit unsigned ints/bytes
#define EMS_VALUE_SHORT_NOTSET -32768 // for 2-byte signed shorts #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_USHORT_NOTSET 0x8000 // for 2-byte unsigned shorts
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs #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 // thermostat specific
#define EMS_THERMOSTAT_MAXHC 4 // max number of heating circuits #define EMS_THERMOSTAT_MAXHC 4 // max number of heating circuits
@@ -49,6 +38,30 @@
#define EMS_THERMOSTAT_WRITE_YES true #define EMS_THERMOSTAT_WRITE_YES true
#define EMS_THERMOSTAT_WRITE_NO false #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 // trigger settings to determine if hot tap water or the heating is active
#define EMS_BOILER_BURNPOWER_TAPWATER 100 #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 #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 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 #define EMS_MODELTYPE_UNKNOWN_STRING "unknown?" // model type text to use when discovering an unknown device
/* EMS UART transfer status */ /* EMS UART transfer status */
@@ -105,6 +108,7 @@ typedef enum {
typedef enum { typedef enum {
EMS_SYS_LOGGING_NONE, // no messages EMS_SYS_LOGGING_NONE, // no messages
EMS_SYS_LOGGING_RAW, // raw data mode 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_BASIC, // only basic read/write messages
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
EMS_SYS_LOGGING_SOLARMODULE, // 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 uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages bool emsPollEnabled; // flag enable the response to poll messages
_EMS_SYS_LOGGING emsLogging; // logging _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 bool emsBusConnected; // is there an active bus
uint32_t emsRxTimestamp; // timestamp of last EMS message received uint32_t emsRxTimestamp; // timestamp of last EMS message received
uint32_t emsPollFrequency; // time between EMS polls 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 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 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 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? unsigned long timestamp; // when created
uint32_t timestamp; // when created
uint8_t data[EMS_MAX_TELEGRAM_LENGTH]; uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
} _EMS_TxTelegram; } _EMS_TxTelegram;
// The Rx receive package // The Rx receive package
typedef struct { typedef struct {
uint32_t timestamp; // timestamp from millis() unsigned long timestamp; // timestamp from millis()
uint8_t * telegram; // the full data package uint8_t * telegram; // the full data package
uint8_t data_length; // length in bytes of the data uint8_t data_length; // length in bytes of the data
uint8_t length; // full length of the complete telegram uint8_t length; // full length of the complete telegram
uint8_t src; // source ID uint8_t src; // source ID
uint8_t dest; // destination 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 offset; // offset
uint8_t * data; // pointer to where telegram data starts uint8_t * data; // pointer to where telegram data starts
bool emsplus; // true if ems+/ems 2.0 bool emsplus; // true if ems+/ems 2.0
@@ -178,55 +182,60 @@ const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
0, // comparisonValue 0, // comparisonValue
0, // comparisonOffset 0, // comparisonOffset
EMS_ID_NONE, // comparisonPostRead EMS_ID_NONE, // comparisonPostRead
false, // forceRefresh
0, // timestamp 0, // timestamp
{0x00} // data {0x00} // data
}; };
// where defintions are stored // flags for triggering changes when EMS data is received
typedef struct { typedef enum : uint8_t {
uint8_t product_id; EMS_DEVICE_UPDATE_FLAG_NONE = 0,
char model_string[70]; EMS_DEVICE_UPDATE_FLAG_BOILER = (1 << 0),
} _Boiler_Device; 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 { typedef enum : uint8_t {
uint8_t product_id; EMS_DEVICE_TYPE_NONE = 0,
char model_string[50]; EMS_DEVICE_TYPE_SERVICEKEY,
} _SolarModule_Device; 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 { typedef struct {
uint8_t product_id; 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; uint8_t device_id;
char model_string[50]; _EMS_DEVICE_TYPE device_type;
} _Other_Device; char device_type_string[30];
} _EMS_Device_Types;
// for storing all recognised EMS devices
typedef struct { typedef struct {
uint8_t product_id; _EMS_DEVICE_TYPE device_type; // type (see above)
char model_string[50]; uint8_t product_id; // product id
} _HeatPump_Device; uint8_t device_id; // device_id
const char * device_desc_p; // pointer to description string in EMS_Devices table
typedef struct { char version[10]; // the version number XX.XX
uint8_t model_id; bool known; // is this a known device?
uint8_t product_id; } _Detected_Device;
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;
/* /*
* Telegram package defintions * Telegram package defintions
@@ -234,6 +243,8 @@ typedef struct {
typedef struct { typedef struct {
// settings // settings
uint8_t device_id; // this is typically always 0x08 uint8_t device_id; // this is typically always 0x08
uint8_t device_flags;
const char * device_desc_p;
uint8_t product_id; uint8_t product_id;
char version[10]; char version[10];
@@ -296,29 +307,18 @@ typedef struct {
*/ */
typedef struct { typedef struct {
uint8_t device_id; // the device ID of the Heat Pump (e.g. 0x30) 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; uint8_t product_id;
char version[10]; char version[10];
uint8_t HPModulation; // heatpump modulation in % uint8_t HPModulation; // heatpump modulation in %
uint8_t HPSpeed; // speed 0-100 % uint8_t HPSpeed; // speed 0-100 %
} _EMS_HeatPump; } _EMS_HeatPump;
// Mixing Module per HC
typedef struct { 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 uint8_t hc; // heating circuit 1, 2, 3 or 4
bool active; // true if there is data for this HC bool active; // true if there is data for this HC
uint16_t flowTemp; uint16_t flowTemp;
uint8_t pumpMod; uint8_t pumpMod;
uint8_t valveStatus; uint8_t valveStatus;
@@ -326,17 +326,22 @@ typedef struct {
// Mixer data // Mixer data
typedef struct { typedef struct {
uint8_t device_id;
uint8_t device_flags;
const char * device_desc_p;
uint8_t product_id;
char version[10];
bool detected; bool detected;
_EMS_Mixing_HC hc[EMS_THERMOSTAT_MAXHC]; // array for the 4 heating circuits _EMS_Mixing_HC hc[EMS_THERMOSTAT_MAXHC]; // array for the 4 heating circuits
} _EMS_Mixing; } _EMS_Mixing;
// SM Solar Module - SM10/SM100/ISM1 // Solar Module - SM10/SM100/ISM1
typedef struct { typedef struct {
uint8_t device_id; // the device ID of the Solar Module 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; uint8_t product_id;
char version[10]; char version[10];
int16_t collectorTemp; // collector temp int16_t collectorTemp; // collector temp
int16_t bottomTemp; // bottom temp int16_t bottomTemp; // bottom temp
uint8_t pumpModulation; // modulation solar pump uint8_t pumpModulation; // modulation solar pump
@@ -368,7 +373,8 @@ typedef struct {
// Thermostat data // Thermostat data
typedef struct { typedef struct {
uint8_t device_id; // the device ID of the thermostat 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; uint8_t product_id;
char version[10]; char version[10];
char datetime[25]; // HH:MM:SS DD/MM/YYYY 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 // Definition for each EMS type, including the relative callback function
typedef struct { typedef struct {
uint8_t model_id; _EMS_DEVICE_UPDATE_FLAG device_flag;
uint16_t type; // long to support EMS+ types uint16_t type;
const char typeString[50]; const char typeString[30];
EMS_processType_cb processType_cb; EMS_processType_cb processType_cb;
} _EMS_Type; } _EMS_Type;
// function definitions // function definitions
extern void ems_dumpBuffer(const char * prefix, uint8_t * telegram, uint8_t length); void ems_dumpBuffer(const char * prefix, uint8_t * telegram, uint8_t length);
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len); void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init(); 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_sendRawTelegram(char * telegram);
void ems_scanDevices(); void ems_scanDevices();
void ems_printAllDevices();
void ems_printDevices(); void ems_printDevices();
uint8_t ems_printDevices_s(char * buffer, uint16_t len); uint8_t ems_printDevices_s(char * buffer, uint16_t len);
void ems_printTxQueue(); void ems_printTxQueue();
@@ -402,27 +407,21 @@ void ems_testTelegram(uint8_t test_num);
void ems_startupTelegrams(); void ems_startupTelegrams();
bool ems_checkEMSBUSAlive(); bool ems_checkEMSBUSAlive();
void ems_clearDeviceList(); void ems_clearDeviceList();
void ems_setThermostatTemp(float temperature, uint8_t hc, uint8_t temptype = 0); void ems_setThermostatTemp(float temperature, uint8_t hc, uint8_t temptype = 0);
void ems_setThermostatMode(uint8_t mode, uint8_t hc); void ems_setThermostatMode(uint8_t mode, uint8_t hc);
void ems_setWarmWaterTemp(uint8_t temperature); void ems_setWarmWaterTemp(uint8_t temperature);
void ems_setFlowTemp(uint8_t temperature); void ems_setFlowTemp(uint8_t temperature);
void ems_setWarmWaterActivated(bool activated); void ems_setWarmWaterActivated(bool activated);
void ems_setWarmWaterOnetime(bool activated);
void ems_setWarmTapWaterActivated(bool activated); void ems_setWarmTapWaterActivated(bool activated);
void ems_setPoll(bool b); void ems_setPoll(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel, bool silent = false); void ems_setLogging(_EMS_SYS_LOGGING loglevel, uint16_t type_id = 0);
void ems_setEmsRefreshed(bool b);
void ems_setWarmWaterModeComfort(uint8_t comfort); void ems_setWarmWaterModeComfort(uint8_t comfort);
void ems_setModels(); void ems_setModels();
void ems_setTxDisabled(bool b); void ems_setTxDisabled(bool b);
void ems_setTxMode(uint8_t mode); void ems_setTxMode(uint8_t mode);
char * ems_getDeviceDescription(_EMS_DEVICE_TYPE device_type, char * buffer, bool name_only = false);
uint8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram); bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer);
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);
void ems_getThermostatValues(); void ems_getThermostatValues();
void ems_getBoilerValues(); void ems_getBoilerValues();
void ems_getSolarModuleValues(); void ems_getSolarModuleValues();
@@ -435,13 +434,15 @@ bool ems_getSolarModuleEnabled();
bool ems_getHeatPumpEnabled(); bool ems_getHeatPumpEnabled();
bool ems_getBusConnected(); bool ems_getBusConnected();
_EMS_SYS_LOGGING ems_getLogging(); _EMS_SYS_LOGGING ems_getLogging();
bool ems_getEmsRefreshed();
uint8_t ems_getThermostatModel(); uint8_t ems_getThermostatModel();
uint8_t ems_getSolarModuleModel(); uint8_t ems_getSolarModuleModel();
void ems_discoverModels(); void ems_discoverModels();
bool ems_getTxCapable(); bool ems_getTxCapable();
uint32_t ems_getPollFrequency(); uint32_t ems_getPollFrequency();
bool ems_getTxDisabled(); 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 // private functions
uint8_t _crcCalculator(uint8_t * data, uint8_t len); 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 _debugPrintPackage(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram, const char * color);
void _ems_clearTxData(); void _ems_clearTxData();
void _removeTxQueue(); void _removeTxQueue();
uint8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram);
// global so can referenced in other classes // global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status; extern _EMS_Sys_Status EMS_Sys_Status;
@@ -456,7 +458,6 @@ extern _EMS_Boiler EMS_Boiler;
extern _EMS_Thermostat EMS_Thermostat; extern _EMS_Thermostat EMS_Thermostat;
extern _EMS_SolarModule EMS_SolarModule; extern _EMS_SolarModule EMS_SolarModule;
extern _EMS_HeatPump EMS_HeatPump; extern _EMS_HeatPump EMS_HeatPump;
extern _EMS_Other EMS_Other;
extern _EMS_Mixing EMS_Mixing; 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" #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 * Common Type
*/ */
@@ -26,13 +63,20 @@
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast #define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast
#define EMS_TYPE_UBAParameterWW 0x33 #define EMS_TYPE_UBAParameterWW 0x33
#define EMS_TYPE_UBATotalUptimeMessage 0x14 #define EMS_TYPE_UBATotalUptimeMessage 0x14
#define EMS_TYPE_UBAFlags 0x35
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15 #define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
#define EMS_TYPE_UBAParametersMessage 0x16 #define EMS_TYPE_UBAParametersMessage 0x16
#define EMS_TYPE_UBASetPoints 0x1A #define EMS_TYPE_UBASetPoints 0x1A
#define EMS_TYPE_UBAFunctionTest 0x1D #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_wwtemp 2 // WW Temperature
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated #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_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_Hot 0x00 // the value for hot
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco #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_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature #define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
// RC35 specific // RC35 specific
#define EMS_TYPE_RC35StatusMessage_HC1 0x3E // is an automatic thermostat broadcast giving us temps on HC1 #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 #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_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 #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_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_setpoint 2 // setpoint temp
#define EMS_OFFSET_JunkersStatusMessage_curr 4 // current 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_pump_mod 5 // pump modulation
#define EMS_OFFSET_MMPLUSStatusMessage_valve_status 2 // valve in percent #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 * Table of all known EMS Devices
* format is PRODUCT ID, DESCRIPTION * 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"}, // UBA Masters - typically with device_id of 0x08
{EMS_PRODUCTID_ISM1, "Junkers ISM1 Solar Module"}, //
{EMS_PRODUCTID_SM50, "SM50 Solar Module"} {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 // Mixing Devices - type 0x20 or 0x21
* Typically device id is 0x20 or 0x21 //
* format is PRODUCT ID, DESCRIPTION {160, EMS_DEVICE_TYPE_MIXING, "MM100 Mixing Module", EMS_DEVICE_FLAG_NONE},
*/ {161, EMS_DEVICE_TYPE_MIXING, "MM200 Mixing Module", EMS_DEVICE_FLAG_NONE},
const _Mixing_Device Mixing_Devices[] = { {69, EMS_DEVICE_TYPE_MIXING, "MM10 Mixer Module", EMS_DEVICE_FLAG_NONE},
{160, "MM100 Mixing Module"}, {159, EMS_DEVICE_TYPE_MIXING, "MM50 Mixing Module", EMS_DEVICE_FLAG_NONE},
{69, "MM10 Mixer Module"}, {79, EMS_DEVICE_TYPE_MIXING, "MM100 Mixer Module", EMS_DEVICE_FLAG_NONE},
{159, "MM50 Mixing Module"}, {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 // HeatPump - type 0x38
const _Other_Device Other_Devices[] = { //
{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"}, // Thermostats, typically device id of 0x10, 0x17 and 0x18
{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[] = {
// Easy devices - not currently supporting write operations // Easy devices - not currently supporting write operations
{EMS_MODEL_EASY, 202, 0x18, "Logamatic TC100/Nefit Moduline Easy", 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
{EMS_MODEL_EASY, 203, 0x18, "Bosch EasyControl CT200", EMS_THERMOSTAT_WRITE_NO}, {203, EMS_DEVICE_TYPE_THERMOSTAT, "Bosch EasyControl CT200", EMS_DEVICE_FLAG_EASY | EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{EMS_MODEL_CW100, 157, 0x18, "Bosch CW100", EMS_THERMOSTAT_WRITE_NO}, {157, EMS_DEVICE_TYPE_THERMOSTAT, "Buderus RC200/Bosch CW100/Junkers CW100", EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Buderus/Nefit // Buderus/Nefit
{EMS_MODEL_RC10, 79, 0x17, "RC10/Moduline 100", EMS_THERMOSTAT_WRITE_YES}, {79, EMS_DEVICE_TYPE_THERMOSTAT, "RC10/Moduline 100", EMS_DEVICE_FLAG_RC10}, // 0x17
{EMS_MODEL_RC20, 77, 0x17, "RC20/Moduline 300", EMS_THERMOSTAT_WRITE_YES}, {77, EMS_DEVICE_TYPE_THERMOSTAT, "RC20/Moduline 300", EMS_DEVICE_FLAG_RC20}, // 0x17
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_WRITE_YES}, {93, EMS_DEVICE_TYPE_THERMOSTAT, "RC20RF", EMS_DEVICE_FLAG_RC20}, // 0x18
{EMS_MODEL_RC30, 67, 0x10, "RC30", EMS_THERMOSTAT_WRITE_YES}, {67, EMS_DEVICE_TYPE_THERMOSTAT, "RC30", EMS_DEVICE_FLAG_RC30}, // 0x10
{EMS_MODEL_RC30, 78, 0x10, "RC30/Moduline 400", EMS_THERMOSTAT_WRITE_YES}, {78, EMS_DEVICE_TYPE_THERMOSTAT, "RC30/Moduline 400", EMS_DEVICE_FLAG_RC30}, // 0x10
{EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_WRITE_YES}, {86, EMS_DEVICE_TYPE_THERMOSTAT, "RC35", EMS_DEVICE_FLAG_RC35}, // 0x10
{EMS_MODEL_RC300, 158, 0x10, "RC300/RC310/Moduline 3000/Bosch CW400", EMS_THERMOSTAT_WRITE_YES}, {158, EMS_DEVICE_TYPE_THERMOSTAT, "RC300/RC310/Moduline 3000/Bosch CW400/W-B Sense II", EMS_DEVICE_FLAG_RC300}, // 0x10
{EMS_MODEL_1010, 165, 0x18, "RC100/Moduline 1010", EMS_THERMOSTAT_WRITE_NO}, {165, EMS_DEVICE_TYPE_THERMOSTAT, "RC100/Moduline 1010", EMS_DEVICE_FLAG_RC300 | EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Sieger // 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 // Junkers
{EMS_MODEL_FW100, 105, 0x10, "Junkers FW100", EMS_THERMOSTAT_WRITE_NO}, {105, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW100", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{EMS_MODEL_FR10, 111, 0x18, "Junkers FR10", EMS_THERMOSTAT_WRITE_NO}, {106, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW200", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{EMS_MODEL_FR100, 105, 0x18, "Junkers FR100", EMS_THERMOSTAT_WRITE_NO}, {107, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR100", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{EMS_MODEL_FR110, 108, 0x18, "Junkers FR110", EMS_THERMOSTAT_WRITE_NO}, {108, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR110", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{EMS_MODEL_FW120, 192, 0x10, "Junkers FW120", EMS_THERMOSTAT_WRITE_NO} {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 // convert bool to text. bools are stored as bytes
char * _bool_to_char(char * s, uint8_t value) { 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)); strlcpy(s, "on", sizeof(s));
} else if (value == EMS_VALUE_INT_OFF) { } else if (value == EMS_VALUE_BOOL_OFF) {
strlcpy(s, "off", sizeof(s)); strlcpy(s, "off", sizeof(s));
} else { } else { // EMS_VALUE_BOOL_NOTSET
strlcpy(s, "?", sizeof(s)); strlcpy(s, "?", sizeof(s));
} }
return s; return s;
@@ -51,22 +51,24 @@ char * _short_to_char(char * s, int16_t value, uint8_t decimals) {
return (s); return (s);
} }
// do floating point
char s2[10] = {0};
// check for negative values // check for negative values
if (value < 0) { if (value < 0) {
strlcpy(s, "-", 10); strlcpy(s, "-", 10);
value *= -1; // convert to positive value *= -1; // convert to positive
} else {
strlcpy(s, "", 10);
} }
// do floating point
char s2[10] = {0};
if (decimals == 2) { if (decimals == 2) {
// divide by 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, ".", 10);
strlcat(s, ((value & 0x01) ? "5" : "0"), 10); strlcat(s, ((value & 0x01) ? "5" : "0"), 10);
} else { } else {
strlcpy(s, ltoa(value / (decimals * 10), s2, 10), 10); strlcat(s, ltoa(value / (decimals * 10), s2, 10), 10);
strlcat(s, ".", 10); strlcat(s, ".", 10);
strlcat(s, ltoa(value % (decimals * 10), s2, 10), 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 // 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) { void _renderShortValue(const char * prefix, const char * postfix, int16_t value, uint8_t decimals) {
static char buffer[200] = {0}; static char buffer[200] = {0};
static char s[20] = {0}; static char s[20] = {0};
@@ -270,7 +272,7 @@ uint8_t _readIntNumber() {
return atoi(numTextPtr); 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() { float _readFloatNumber() {
char * numTextPtr = strtok(nullptr, ", \n"); char * numTextPtr = strtok(nullptr, ", \n");
if (numTextPtr == nullptr) { if (numTextPtr == nullptr) {

View File

@@ -13,7 +13,7 @@
#define EMSUART_CONFIG 0x1C // 8N1 (8 bits, no stop bits, 1 parity) #define EMSUART_CONFIG 0x1C // 8N1 (8 bits, no stop bits, 1 parity)
#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit #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 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 #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 "boiler_cmd" // for receiving boiler commands via MQTT
#define TOPIC_BOILER_CMD_WWACTIVATED "boiler_cmd_wwactivated" // change water on/off #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_WWTEMP "boiler_cmd_wwtemp" // wwtemp changes via MQTT
#define TOPIC_BOILER_CMD_COMFORT "comfort" // ww comfort setting via MQTT #define TOPIC_BOILER_CMD_COMFORT "comfort" // ww comfort setting via MQTT
#define TOPIC_BOILER_CMD_FLOWTEMP "flowtemp" // flowtemp value 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 "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 "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 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> </li>
</ul> </ul>
</li> </li>
<li>
<a href="#" id="eventlog"><i class="glyphicon glyphicon-transfer"></i>Event Log</a>
</li>
<li> <li>
<a href="#" id="backup"><i class="glyphicon glyphicon-floppy-disk"></i>Backup & Restore</a> <a href="#" id="backup"><i class="glyphicon glyphicon-floppy-disk"></i>Backup & Restore</a>
</li> </li>
@@ -194,7 +191,10 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div> <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"> <div id="onlineupdate">
<h5 id=releasehead></h5> <h5 id=releasehead></h5>
<div style="clear:both;"> <div style="clear:both;">

View File

@@ -100,7 +100,6 @@
</span> </span>
<br> <br>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-xs-3">Serial Port<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" <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" aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
@@ -115,11 +114,10 @@
</div> </div>
<br> <br>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-xs-3">Event Logging<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" <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" 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"> <div class="col-xs-9">
<form> <form>
<label class="radio-inline"> <label class="radio-inline">
@@ -128,6 +126,16 @@
<input type="radio" value="0" name="logeventsenabled" checked>Disabled</label> <input type="radio" value="0" name="logeventsenabled" checked>Disabled</label>
</form> </form>
</div> </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>
<br> <br>
<div class="col-xs-9 col-md-8"> <div class="col-xs-9 col-md-8">
@@ -142,32 +150,6 @@
<br> <br>
</div> </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"> <div id="mqttcontent">
<br> <br>
<legend>MQTT Settings</legend> <legend>MQTT Settings</legend>
@@ -308,10 +290,10 @@
</form> </form>
</div> </div>
</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" <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" 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"> <span class="col-xs-7 col-md-5">
<input class="form-control input-sm" id="inputtohide" type="text" name="ap_ssid"> <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> <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;" <button id="scanb" type="button" class="btn btn-info btn-xs" style="display:none;"
onclick="scanWifi()">Scan...</button> onclick="scanWifi()">Scan...</button>
</span> </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" <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" 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"> <span class="col-xs-9 col-md-5">
<input id="wifipass" class="form-control input-sm" name="ap_passwd" type="password"> <input id="wifipass" class="form-control input-sm" name="ap_passwd" type="password">
</span> </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> <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"> <div class="col-xs-9 col-md-8">
<button onclick="savenetwork()" class="btn btn-primary btn-sm pull-right">Save</button> <button onclick="savenetwork()" class="btn btn-primary btn-sm pull-right">Save</button>
</div> </div>
@@ -340,27 +358,17 @@
<legend>Time Settings</legend> <legend>Time Settings</legend>
<h6 class="text-muted">With Network Time Protocol (NTP) enabled, all times are adjusted to the local timezone <h6 class="text-muted">With Network Time Protocol (NTP) enabled, all times are adjusted to the local timezone
and and
respect daylight saving respect daylight saving time (DST)</h6>
time (DST)</h6>
<br> <br>
<div class="row form-group"> <div class="row form-group">
<label class="col-xs-3">Device Time</label> <label class="col-xs-3">Device Time</label>
<span id="utc" class="col-xs-9 col-md-5"> <span id="utc" class="col-xs-9 col-md-5"></span>
</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">
<div class="col-xs-3"> <div class="col-xs-3">
<button onclick="syncBrowserTime()" class="btn btn-info btn-sm">Sync Browser Time to Device</button><i <button onclick="forcentp()" id="forcentp" class="btn btn-info btn-sm">Sync Device with Internet
style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" aria-hidden="true" Time</button>
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>
</div> </div>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-xs-3">NTP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign" <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" aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
@@ -372,8 +380,6 @@
<label class="radio-inline"> <label class="radio-inline">
<input type="radio" value="0" onclick="handleNTPOFF();" name="ntpenabled" checked>Disabled</label> <input type="radio" value="0" onclick="handleNTPOFF();" name="ntpenabled" checked>Disabled</label>
</form> </form>
<button onclick="forcentp()" id="forcentp" class="btn btn-info btn-sm">Sync Device with Internet
Time</button>
</div> </div>
</div> </div>
<div class="row form-group"> <div class="row form-group">
@@ -391,10 +397,30 @@
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right" aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="Time in minutes between scheduled NTP refreshes"></i></label> data-content="Time in minutes between scheduled NTP refreshes"></i></label>
<span class="col-xs-9 col-md-5"> <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> </span>
<br> <br>
</div> </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> <br>
<div class="col-xs-9 col-md-8"> <div class="col-xs-9 col-md-8">
<button onclick="saventp()" class="btn btn-primary btn-sm pull-right">Save</button> <button onclick="saventp()" class="btn btn-primary btn-sm pull-right">Save</button>
@@ -477,26 +503,22 @@
</tr> </tr>
</table> </table>
</div> </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 panel-default table-responsive">
<div class="panel-title" id="mqttloghdr"></div> <table class="table table-hover table-striped table-condensed" border=1>
</div> <caption>MQTT&nbsp;&nbsp;<div id="mqttconnected"></div>&nbsp;<div id="mqttheartbeat"></div>
<div> </caption>
<table id="mqttlogtable" class="table" data-paging="false" data-filtering="false" <thead>
data-sorting="false" data-editing="false" data-state="true"></table> <tr>
</div> <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> </div>
</div> </div>

View File

@@ -2,78 +2,35 @@ var version = "";
var websock = null; var websock = null;
var wsUri = "ws://" + window.location.host + "/ws"; var wsUri = "ws://" + window.location.host + "/ws";
var utcSeconds; var ntpSeconds;
var data = [];
var ajaxobj; var ajaxobj;
var custom_config = {}; var custom_config = {};
var config = { var xDown = null;
"command": "configfile", var yDown = null;
"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 page = 1;
var haspages;
var file = {}; var file = {};
var backupstarted = false; var backupstarted = false;
var updateurl = ""; var updateurl = "";
var updateurl_dev = "";
var use_beta_firmware = false;
var myespcontent; var myespcontent;
function browserTime() { var formData = new FormData();
var d = new Date(0);
var c = new Date(); var nextIsNotJson = false;
var timestamp = Math.floor((c.getTime() / 1000) + ((c.getTimezoneOffset() * 60) * -1));
d.setUTCSeconds(timestamp); var config = {};
document.getElementById("rtc").innerHTML = d.toUTCString().slice(0, -3);
}
function deviceTime() { 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 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(ntpSeconds);
t.setUTCSeconds(devTime);
document.getElementById("utc").innerHTML = t.toUTCString().slice(0, -3); 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() { function handleNTPON() {
document.getElementById("forcentp").style.display = "block"; document.getElementById("forcentp").style.display = "block";
} }
@@ -86,7 +43,8 @@ function listntp() {
websock.send("{\"command\":\"gettime\"}"); websock.send("{\"command\":\"gettime\"}");
document.getElementById("ntpserver").value = config.ntp.server; 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) { if (config.ntp.enabled) {
$("input[name=\"ntpenabled\"][value=\"1\"]").prop("checked", true); $("input[name=\"ntpenabled\"][value=\"1\"]").prop("checked", true);
@@ -127,7 +85,8 @@ function custom_saveconfig() {
function saventp() { function saventp() {
config.ntp.server = document.getElementById("ntpserver").value; 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; config.ntp.enabled = false;
if (parseInt($("input[name=\"ntpenabled\"]:checked").val()) === 1) { if (parseInt($("input[name=\"ntpenabled\"]:checked").val()) === 1) {
@@ -160,6 +119,8 @@ function savegeneral() {
config.general.log_events = true; config.general.log_events = true;
} }
config.general.log_ip = document.getElementById("log_ip").value;
saveconfig(); saveconfig();
} }
@@ -205,12 +166,14 @@ function savenetwork() {
config.network.wmode = wmode; config.network.wmode = wmode;
config.network.password = document.getElementById("wifipass").value; 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(); saveconfig();
} }
var formData = new FormData();
function inProgress(callback) { function inProgress(callback) {
$("body").load("myesp.html #progresscontent", function (responseTxt, statusTxt, xhr) { $("body").load("myesp.html #progresscontent", function (responseTxt, statusTxt, xhr) {
if (statusTxt === "success") { if (statusTxt === "success") {
@@ -283,15 +246,13 @@ function inProgressUpload() {
function handleSTA() { function handleSTA() {
document.getElementById("scanb").style.display = "block"; document.getElementById("scanb").style.display = "block";
document.getElementById("hidessid").style.display = "block"; document.getElementById("hideclient").style.display = "block";
document.getElementById("hidepasswd").style.display = "block";
} }
function handleAP() { function handleAP() {
document.getElementById("ssid").style.display = "none"; document.getElementById("ssid").style.display = "none";
document.getElementById("scanb").style.display = "none"; document.getElementById("scanb").style.display = "none";
document.getElementById("hidessid").style.display = "none"; document.getElementById("hideclient").style.display = "none";
document.getElementById("hidepasswd").style.display = "none";
document.getElementById("inputtohide").style.display = "block"; document.getElementById("inputtohide").style.display = "block";
} }
@@ -307,6 +268,11 @@ function listnetwork() {
handleSTA(); 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() { function listgeneral() {
@@ -320,6 +286,9 @@ function listgeneral() {
if (config.general.log_events) { if (config.general.log_events) {
$("input[name=\"logeventsenabled\"][value=\"1\"]").prop("checked", true); $("input[name=\"logeventsenabled\"][value=\"1\"]").prop("checked", true);
} }
document.getElementById("log_ip").value = config.general.log_ip;
} }
function listmqtt() { function listmqtt() {
@@ -375,32 +344,10 @@ function scanWifi() {
} }
} }
function getEvents() {
websock.send("{\"command\":\"geteventlog\", \"page\":" + page + "}");
}
function isVisible(e) { function isVisible(e) {
return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length); 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) { function colorStatusbar(ref) {
var percentage = ref.style.width.slice(0, -1); 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"; } 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("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) { function getContent(contentname) {
@@ -471,19 +450,6 @@ function getContent(contentname) {
case "#networkcontent": case "#networkcontent":
listnetwork(); listnetwork();
break; 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": case "#customcontent":
listcustom(); listcustom();
break; break;
@@ -510,6 +476,7 @@ function getContent(contentname) {
$("#appurl2").text(ajaxobj.appurl); $("#appurl2").text(ajaxobj.appurl);
updateurl = ajaxobj.updateurl; updateurl = ajaxobj.updateurl;
updateurl_dev = ajaxobj.updateurl_dev;
listCustomStats(); listCustomStats();
break; break;
default: default:
@@ -602,147 +569,12 @@ function twoDigits(value) {
return 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) { function socketMessageListener(evt) {
var obj = JSON.parse(evt.data); var obj = JSON.parse(evt.data);
if (obj.hasOwnProperty("command")) { if (obj.hasOwnProperty("command")) {
switch (obj.command) { switch (obj.command) {
case "status": case "status":
ajaxobj = obj; ajaxobj = obj;
initMQTTLogTable();
getContent("#statuscontent"); getContent("#statuscontent");
break; break;
case "custom_settings": case "custom_settings":
@@ -752,17 +584,8 @@ function socketMessageListener(evt) {
ajaxobj = obj; ajaxobj = obj;
getContent("#custom_statuscontent"); getContent("#custom_statuscontent");
break; break;
case "eventlist":
haspages = obj.haspages;
if (haspages === 0) {
document.getElementById("loading-img").style.display = "none";
initEventTable();
break;
}
builddata(obj);
break;
case "gettime": case "gettime":
utcSeconds = obj.epoch; ntpSeconds = obj.epoch;
deviceTime(); deviceTime();
break; break;
case "ssidlist": case "ssidlist":
@@ -778,26 +601,6 @@ function socketMessageListener(evt) {
break; 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() { function compareDestroy() {
@@ -814,46 +617,6 @@ function restart() {
inProgress("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) { function handleTouchStart(evt) {
xDown = evt.touches[0].clientX; xDown = evt.touches[0].clientX;
yDown = evt.touches[0].clientY; 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() { 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 asset = release.assets[0];
var downloadCount = 0; var downloadCount = 0;
for (var i = 0; i < release.assets.length; i++) { 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>"); }); }).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() { function allowUpload() {
$("#upbtn").prop("disabled", false); $("#upbtn").prop("disabled", false);
} }
@@ -1006,7 +783,7 @@ function start() {
}); });
} }
function refreshEMS() { function refreshCustomStatus() {
websock.send("{\"command\":\"custom_status\"}"); websock.send("{\"command\":\"custom_status\"}");
} }
@@ -1014,5 +791,30 @@ function refreshStatus() {
websock.send("{\"command\":\"status\"}"); 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("touchstart", handleTouchStart, false);
document.addEventListener("touchmove", handleTouchMove, false); document.addEventListener("touchmove", handleTouchMove, false);

View File

@@ -100,7 +100,7 @@ gulp.task('myespjs', function () {
}); });
gulp.task('requiredjs', 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({ .pipe(concat({
path: 'required.js', path: 'required.js',
stat: { stat: {
@@ -117,7 +117,7 @@ gulp.task('requiredjs', function () {
gulp.task('requiredcss', 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({ .pipe(concat({
path: 'required.css', path: 'required.css',
stat: { 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 = { var configfile = {
"command": "configfile", "command": "configfile",
"network": { "network": {
"ssid": "myssid", "ssid": "myssid",
"wmode": 0, "wmode": 0,
"password": "password" "password": "password",
"password": "",
"staticip": "",
"gatewayip": "",
"nmask": "",
"dnsip": ""
}, },
"general": { "general": {
"hostname": "myesp", "hostname": "ems-esp",
"password": "admin", "password": "admin",
"serial": true, "serial": true,
"log_events": true "version": "1.0.0",
"log_events": false,
"log_ip": "10.11.12.13"
}, },
"mqtt": { "mqtt": {
"enabled": false, "enabled": false,
@@ -86,8 +81,9 @@ var configfile = {
}, },
"ntp": { "ntp": {
"server": "pool.ntp.org", "server": "pool.ntp.org",
"interval": "30", "interval": 720,
"enabled": false "timezone": 2,
"enabled": true
} }
}; };
@@ -101,21 +97,11 @@ var custom_configfile = {
"listen_mode": false, "listen_mode": false,
"shower_timer": true, "shower_timer": true,
"shower_alert": false, "shower_alert": false,
"publish_time": 120, "publish_time": 0,
"tx_mode": 1 "tx_mode": 1
} }
}; };
function sendEventLog() {
wss.broadcast(eventlog);
var res = {
"command": "result",
"resultof": "eventlist",
"result": true
};
wss.broadcast(res);
}
function sendStatus() { function sendStatus() {
var stats = { var stats = {
"command": "status", "command": "status",
@@ -127,7 +113,7 @@ function sendStatus() {
"availsize": 2469, "availsize": 2469,
"ip": "10.10.10.198", "ip": "10.10.10.198",
"ssid": "my_ssid", "ssid": "my_ssid",
"mac": "DC:4F:11:22:93:06", "mac": "DC:4F:12:22:13:06",
"signalstr": 62, "signalstr": 62,
"systemload": 0, "systemload": 0,
"mqttconnected": true, "mqttconnected": true,
@@ -155,6 +141,7 @@ function sendCustomStatus() {
"customname": "EMS-ESP", "customname": "EMS-ESP",
"appurl": "https://github.com/proddy/EMS-ESP", "appurl": "https://github.com/proddy/EMS-ESP",
"updateurl": "https://api.github.com/repos/proddy/EMS-ESP/releases/latest", "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": { "emsbus": {
"ok": true, "ok": true,
@@ -241,28 +228,13 @@ wss.on('connection', function connection(ws) {
console.log("[INFO] Sending time"); console.log("[INFO] Sending time");
var res = {}; var res = {};
res.command = "gettime"; res.command = "gettime";
res.epoch = Math.floor((new Date).getTime() / 1000); res.epoch = 1572613374; // this is 13:02:54 CET
//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);
wss.broadcast(res); wss.broadcast(res);
break; break;
case "getconf": case "getconf":
console.log("[INFO] Sending system configuration file (if set any)"); console.log("[INFO] Sending system configuration file (if set any)");
wss.broadcast(configfile); wss.broadcast(configfile);
break; break;
case "geteventlog":
console.log("[INFO] Sending eventlog");
sendEventLog();
break;
case "clearevent":
console.log("[INFO] Clearing eventlog");
break;
case "restart": case "restart":
console.log("[INFO] Restart"); console.log("[INFO] Restart");
break; break;