mirror of
https://github.com/arendst/Tasmota.git
synced 2025-04-24 23:07:17 +00:00
Merge remote-tracking branch 'upstream/development' into DimmerNewState
This commit is contained in:
commit
535900f9f3
14
.github/workflows/Tasmota_build_development.yml
vendored
14
.github/workflows/Tasmota_build_development.yml
vendored
@ -1351,3 +1351,17 @@ jobs:
|
||||
destination_repo: 'arendst/Tasmota-firmware'
|
||||
user_email: 'github-actions@github.com'
|
||||
user_name: 'github-actions'
|
||||
- name: Creat trigger.txt
|
||||
run: |
|
||||
echo ${GITHUB_SHA} &> trigger.txt
|
||||
echo "$(<trigger.txt)"
|
||||
- name: Push trigger.txt to start workflow in repo https://github.com/arendst/Tasmota-firmware
|
||||
uses: dmnemec/copy_file_to_another_repo_action@main
|
||||
env:
|
||||
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
|
||||
with:
|
||||
source_file: 'trigger.txt'
|
||||
destination_repo: 'arendst/Tasmota-firmware'
|
||||
destination_branch: 'action-development'
|
||||
user_email: 'github-actions@github.com'
|
||||
user_name: 'github-actions'
|
||||
|
14
.github/workflows/Tasmota_build_master.yml
vendored
14
.github/workflows/Tasmota_build_master.yml
vendored
@ -1358,3 +1358,17 @@ jobs:
|
||||
destination_repo: 'arendst/Tasmota-firmware'
|
||||
user_email: 'github-actions@github.com'
|
||||
user_name: 'github-actions'
|
||||
- name: Creat trigger.txt
|
||||
run: |
|
||||
echo ${GITHUB_SHA} &> trigger.txt
|
||||
echo "$(<trigger.txt)"
|
||||
- name: Push trigger.txt to start workflow in repo https://github.com/arendst/Tasmota-firmware
|
||||
uses: dmnemec/copy_file_to_another_repo_action@main
|
||||
env:
|
||||
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
|
||||
with:
|
||||
source_file: 'trigger.txt'
|
||||
destination_repo: 'arendst/Tasmota-firmware'
|
||||
destination_branch: 'action-master'
|
||||
user_email: 'github-actions@github.com'
|
||||
user_name: 'github-actions'
|
||||
|
@ -8,7 +8,10 @@ All notable changes to this project will be documented in this file.
|
||||
- Turn HTTP API (command ``SetOption128 1``) default on for backward compatibility
|
||||
- Support for IEM3155 Wattmeter (#12940)
|
||||
- Berry support for vararg
|
||||
- Switchmode11 and 12: ``SetOption129 1`` delays single press events from pressing to release switch (#12712 / #12713)
|
||||
- Command ``Subscribe2 ...`` to subscribe to a MQTT topic without appended "/#" (#12858)
|
||||
- Support for Hydreon RG-15 Solid State Rain sensor (#12974)
|
||||
- Support for IKEA VINDRIKTNING particle concentration sensor (#12976)
|
||||
- Commands ``SwitchMode 17`` PushHoldMultiDelay and ``SwitchMode 18`` PushHoldMultiDelayInverted adding delayed single press event (#12973)
|
||||
|
||||
### Changed
|
||||
- Shelly EM template needs to use GPIO ADE7953_IRQ_2
|
||||
@ -18,6 +21,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Shelly 2.5 negative power values on relay 1 regression from 9.5.0.5
|
||||
- Wiegand support for keypad zero key in single key mode using ``SetOption124 1`` (#12960)
|
||||
- Hass and Tasmota discovery prefix topic notifications (#12972)
|
||||
- Unable to disable MusicSync mode on Sonoff L1 Lite regression from 9.3.0 (#12930)
|
||||
|
||||
## [9.5.0.6] 20210820
|
||||
### Added
|
||||
@ -1034,7 +1038,7 @@ All notable changes to this project will be documented in this file.
|
||||
## [8.1.0.6] - 20200205
|
||||
### Added
|
||||
- Support for sensors DS18x20 and DHT family on Shelly 1 and Shelly 1PM using Shelly Add-On adapter (#7469)
|
||||
- Commands ``SwitchMode 11`` PushHoldMulti and ``SwitchMode 12`` PushHoldInverted (#7603)
|
||||
- Commands ``SwitchMode 11`` PushHoldMulti and ``SwitchMode 12`` PushHoldMultiInverted (#7603)
|
||||
- Command ``Buzzer -1`` for infinite mode and command ``Buzzer -2`` for following led mode (#7623)
|
||||
- Support for MI-BLE sensors using HM-10 Bluetooth 4.0 module by Christian Staars (#7683)
|
||||
- BootCount Reset Time as BCResetTime to ``Status 1``
|
||||
|
@ -104,6 +104,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
|
||||
- Command ``SetOption127 1`` to force Wi-Fi in no-sleep mode even if ``Sleep 0`` is not enabled
|
||||
- Command ``SetOption128 0|1`` web referer check disabling HTTP API commands if set to 0. Default set to 1 for backward compatibility [#12828](https://github.com/arendst/Tasmota/issues/12828)
|
||||
- Command ``SetSensor1..127 0|1`` to globally disable individual sensor driver
|
||||
- Commands ``SwitchMode 17`` PushHoldMultiDelay and ``SwitchMode 18`` PushHoldMultiDelayInverted adding delayed single press event [#12973](https://github.com/arendst/Tasmota/issues/12973)
|
||||
- Neopool commands ``NPPHRes``, ``NPCLRes`` and ``NPIonRes`` [#12813](https://github.com/arendst/Tasmota/issues/12813)
|
||||
- Support for second DNS server
|
||||
- Support for (Yeelight) Mi Desk Pro using binary tasmota32solo1.bin
|
||||
@ -120,7 +121,10 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
|
||||
- Support for CAN bus and Freedom Won Battery Management System by Marius Bezuidenhout [#12651](https://github.com/arendst/Tasmota/issues/12651)
|
||||
- Optional IP filter to command ``TCPStart`` [#12806](https://github.com/arendst/Tasmota/issues/12806)
|
||||
- Inital support for Wi-Fi extender [#12784](https://github.com/arendst/Tasmota/issues/12784)
|
||||
- Command ``Subscribe2 ...`` to subscribe to a MQTT topic without appended "/#" [#12858](https://github.com/arendst/Tasmota/issues/12858)
|
||||
- Support for IEM3155 Wattmeter [#12940](https://github.com/arendst/Tasmota/issues/12940)
|
||||
- Support for Hydreon RG-15 Solid State Rain sensor [#12974](https://github.com/arendst/Tasmota/issues/12974)
|
||||
- Support for IKEA VINDRIKTNING particle concentration sensor [#12976](https://github.com/arendst/Tasmota/issues/12976)
|
||||
|
||||
### Changed
|
||||
- Move firmware binaries to https://github.com/arendst/Tasmota-firmware/tree/main/release-firmware
|
||||
@ -169,5 +173,6 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
|
||||
- Neopool communication error [#12813](https://github.com/arendst/Tasmota/issues/12813)
|
||||
- WDT reset on shutters with stepper motors during deceleration [#12849](https://github.com/arendst/Tasmota/issues/12849)
|
||||
- Negative power values for ADE7953 based devices like Shelly EM [#12874](https://github.com/arendst/Tasmota/issues/12874)
|
||||
- Unable to disable MusicSync mode on Sonoff L1 Lite regression from 9.3.0 [#12930](https://github.com/arendst/Tasmota/issues/12930)
|
||||
- Wiegand support for keypad zero key in single key mode using ``SetOption124 1`` [#12960](https://github.com/arendst/Tasmota/issues/12960)
|
||||
- Hass and Tasmota discovery prefix topic notifications [#12972](https://github.com/arendst/Tasmota/issues/12972)
|
||||
|
@ -4,7 +4,7 @@
|
||||
"ldscript": "esp32_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": "-DARDUINO_ESP32_DEV -DESP32_16M",
|
||||
"extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DESP32_16M",
|
||||
"f_cpu": "80000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dout",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"ldscript": "esp32_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": "-DARDUINO_ESP32_DEV -DESP32_4M",
|
||||
"extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DESP32_4M",
|
||||
"f_cpu": "80000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dout",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"ldscript": "esp32_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": "-DARDUINO_ESP32_DEV -DESP32_8M",
|
||||
"extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DESP32_8M",
|
||||
"f_cpu": "80000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dout",
|
||||
|
@ -1,36 +1,37 @@
|
||||
#include "be_constobj.h"
|
||||
|
||||
static be_define_const_map_slots(be_class_bytes_map) {
|
||||
{ be_const_key(dot_p, -1), be_const_var(0) },
|
||||
{ be_const_key(seti, 6), be_const_func(m_set) },
|
||||
{ be_const_key(get, 7), be_const_func(m_getu) },
|
||||
{ be_const_key(size, 10), be_const_func(m_size) },
|
||||
{ be_const_key(resize, 1), be_const_func(m_resize) },
|
||||
{ be_const_key(opt_add, 22), be_const_func(m_merge) },
|
||||
{ be_const_key(getbits, -1), be_const_closure(getbits_closure) },
|
||||
{ be_const_key(geti, 16), be_const_func(m_geti) },
|
||||
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
|
||||
{ be_const_key(add, -1), be_const_func(m_add) },
|
||||
{ be_const_key(fromb64, -1), be_const_func(m_fromb64) },
|
||||
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
|
||||
{ be_const_key(set, -1), be_const_func(m_set) },
|
||||
{ be_const_key(asstring, -1), be_const_func(m_asstring) },
|
||||
{ be_const_key(copy, 3), be_const_func(m_copy) },
|
||||
{ be_const_key(opt_eq, 2), be_const_func(m_equal) },
|
||||
{ be_const_key(tob64, -1), be_const_func(m_tob64) },
|
||||
{ be_const_key(setbits, 12), be_const_closure(setbits_closure) },
|
||||
{ be_const_key(_buffer, -1), be_const_func(m_buffer) },
|
||||
{ be_const_key(fromstring, 0), be_const_func(m_fromstring) },
|
||||
{ be_const_key(tostring, 9), be_const_func(m_tostring) },
|
||||
{ be_const_key(item, 8), be_const_func(m_item) },
|
||||
{ be_const_key(init, 23), be_const_func(m_init) },
|
||||
{ be_const_key(deinit, -1), be_const_func(m_deinit) },
|
||||
{ be_const_key(opt_connect, -1), be_const_func(m_connect) },
|
||||
{ be_const_key(item, 3), be_const_func(m_item) },
|
||||
{ be_const_key(seti, 22), be_const_func(m_set) },
|
||||
{ be_const_key(asstring, -1), be_const_func(m_asstring) },
|
||||
{ be_const_key(getbits, -1), be_const_closure(getbits_closure) },
|
||||
{ be_const_key(copy, -1), be_const_func(m_copy) },
|
||||
{ be_const_key(get, -1), be_const_func(m_getu) },
|
||||
{ be_const_key(_buffer, -1), be_const_func(m_buffer) },
|
||||
{ be_const_key(fromb64, -1), be_const_func(m_fromb64) },
|
||||
{ be_const_key(add, -1), be_const_func(m_add) },
|
||||
{ be_const_key(dot_p, -1), be_const_var(0) },
|
||||
{ be_const_key(size, 0), be_const_func(m_size) },
|
||||
{ be_const_key(tostring, -1), be_const_func(m_tostring) },
|
||||
{ be_const_key(opt_add, 10), be_const_func(m_merge) },
|
||||
{ be_const_key(resize, -1), be_const_func(m_resize) },
|
||||
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
|
||||
{ be_const_key(set, -1), be_const_func(m_set) },
|
||||
{ be_const_key(geti, -1), be_const_func(m_geti) },
|
||||
{ be_const_key(init, -1), be_const_func(m_init) },
|
||||
{ be_const_key(clear, -1), be_const_func(m_clear) },
|
||||
{ be_const_key(opt_neq, 23), be_const_func(m_nequal) },
|
||||
{ be_const_key(tob64, 24), be_const_func(m_tob64) },
|
||||
{ be_const_key(opt_eq, -1), be_const_func(m_equal) },
|
||||
{ be_const_key(fromstring, -1), be_const_func(m_fromstring) },
|
||||
{ be_const_key(setbits, -1), be_const_closure(setbits_closure) },
|
||||
};
|
||||
|
||||
static be_define_const_map(
|
||||
be_class_bytes_map,
|
||||
25
|
||||
26
|
||||
);
|
||||
|
||||
BE_EXPORT_VARIABLE be_define_const_class(
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "be_mem.h"
|
||||
#include "be_gc.h"
|
||||
#include "be_class.h"
|
||||
#include "be_vector.h"
|
||||
#include "be_string.h"
|
||||
#include "be_map.h"
|
||||
#include <string.h>
|
||||
|
||||
#define READLINE_STEP 100
|
||||
@ -73,36 +76,105 @@ static int l_input(bvm *vm)
|
||||
return m_readline(vm);
|
||||
}
|
||||
|
||||
/* Look in the current class and all super classes for a method corresponding to a specific closure pointer */
|
||||
static bclass *find_class_closure(bclass *cl, bclosure *needle)
|
||||
{
|
||||
while (cl) {
|
||||
bmapnode *node; /* iterate on members of the class */
|
||||
bmap *members = be_class_members(cl);
|
||||
if (members) { /* only iterate if there are members */
|
||||
bmapiter iter = be_map_iter();
|
||||
while ((node = be_map_next(members, &iter)) != NULL) {
|
||||
if (var_type(&node->value) == BE_CLOSURE) { /* only native functions are considered */
|
||||
bclosure *clos_iter = var_toobj(&node->value); /* retrieve the method's closure */
|
||||
if (clos_iter == needle) {
|
||||
/* we found the closure, we now know its class */
|
||||
return cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cl = be_class_super(cl); /* move to super class */
|
||||
}
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
static int l_super(bvm *vm)
|
||||
{
|
||||
int argc = be_top(vm);
|
||||
if (argc) {
|
||||
|
||||
/* if no argument, or arg 1 is nil, return nil */
|
||||
if (argc == 0 || be_isnil(vm, 1)) {
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
/* if arg 1 is a class, simply return super */
|
||||
if (be_isclass(vm, 1)) {
|
||||
be_getsuper(vm, 1);
|
||||
be_return(vm);
|
||||
}
|
||||
|
||||
/* arg 1 is an instance */
|
||||
if (be_isinstance(vm, 1)) {
|
||||
binstance *o = var_toobj(be_indexof(vm, 1));
|
||||
bclass *target_class = NULL; /* the minimal class expected, or any super class */
|
||||
bclass *base_class = NULL; /* current class of the caller, if any */
|
||||
|
||||
/* if arg 2 is present, it must be a class */
|
||||
if (argc >= 2) {
|
||||
if (be_isinstance(vm, 1) && be_isclass(vm, 2)) {
|
||||
/* leveled super, i.e. fix the parenthood class level */
|
||||
binstance *o = var_toobj(be_indexof(vm, 1));
|
||||
bclass *bc = var_toobj(be_indexof(vm, 2));
|
||||
while (o) {
|
||||
bclass *c = be_instance_class(o);
|
||||
if (c == bc) break; /* found */
|
||||
o = be_instance_super(o);
|
||||
}
|
||||
bvalue *top = be_incrtop(vm);
|
||||
if (o) {
|
||||
var_setinstance(top, o); /* return the instance with the specified parent class */
|
||||
} else {
|
||||
var_setnil(top); /* not found, return nil */
|
||||
}
|
||||
be_return(vm);
|
||||
if (be_isclass(vm, 2)) {
|
||||
target_class = var_toobj(be_indexof(vm, 2));
|
||||
} else if (be_isnil(vm, 2)) {
|
||||
// ignore, revert to standard super() behavior if second arg is explicit nil
|
||||
} else {
|
||||
be_raise(vm, "type_error", "leveled super() requires 'instance' and 'class' arguments");
|
||||
}
|
||||
}
|
||||
|
||||
/* now the more complex part, if arg 1 is an instance */
|
||||
/* if instance is the sole argument, try to find if it comes from a method of a class and set 'base_class' accordinly */
|
||||
/* later it will be equivalent to passing this class as second argument */
|
||||
if (argc == 1) {
|
||||
/* we look in the callstack for the caller's closure */
|
||||
int size = be_stack_count(&vm->callstack);
|
||||
if (size >= 2) { /* need at least 2 stackframes: current (for super() native) and caller (the one we are interested in) */
|
||||
bcallframe *caller = be_vector_at(&vm->callstack, size - 2); /* get the callframe of caller */
|
||||
bvalue *func = caller->func; /* function object of caller */
|
||||
if (var_type(func) == BE_CLOSURE) { /* only useful if the caller is a Berry closure (i.e. not native) */
|
||||
bclosure *clos_ctx = var_toobj(func); /* this is the closure we look for in the class chain */
|
||||
base_class = find_class_closure(o->_class, clos_ctx); /* iterate on current and super classes to find where the closure belongs */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (base_class || target_class) {
|
||||
if (base_class) {
|
||||
target_class = base_class->super;
|
||||
if (!target_class) be_return_nil(vm); /* fast exit if top class */
|
||||
}
|
||||
/* leveled super, i.e. fix the parenthood class level */
|
||||
if (o) {
|
||||
o = be_instance_super(o); /* always skip the current class and move to super */
|
||||
}
|
||||
while (o) {
|
||||
bclass *c = be_instance_class(o);
|
||||
if (c == target_class) break; /* found */
|
||||
o = be_instance_super(o);
|
||||
}
|
||||
bvalue *top = be_incrtop(vm);
|
||||
if (o) {
|
||||
var_setinstance(top, o); /* return the instance with the specified parent class */
|
||||
} else {
|
||||
var_setnil(top); /* not found, return nil */
|
||||
}
|
||||
be_return(vm);
|
||||
} else {
|
||||
/* simple use of super */
|
||||
be_getsuper(vm, 1);
|
||||
be_return(vm);
|
||||
}
|
||||
}
|
||||
|
||||
/* fall through, return nil if we don't know what to do */
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
|
@ -137,14 +137,21 @@ static bstring** save_members(bvm *vm, void *fp, bclass *c, int nvar)
|
||||
} else { /* save method's name and function */
|
||||
bproto *proto;
|
||||
bvalue *value = &node->value;
|
||||
be_assert(var_isclosure(value) || var_isproto(value));
|
||||
be_assert(var_isclosure(value) || var_isproto(value) || var_isnil(value));
|
||||
save_string(fp, var_tostr(&node->key)); /* save method name */
|
||||
if (var_isproto(value)) { /* the method is a prototype */
|
||||
proto = var_toobj(value);
|
||||
} else { /* the method is a closure */
|
||||
proto = cast(bclosure *, var_toobj(value))->proto;
|
||||
save_proto(vm, fp, proto); /* only save prototype */
|
||||
} else if (var_isclosure(value)) { /* the method is a closure */
|
||||
proto = cast(bclosure *, var_toobj(value))->proto;
|
||||
save_proto(vm, fp, proto); /* only save prototype */
|
||||
} else if (var_isnil(value)) {
|
||||
/* this is a static member (nil default) */
|
||||
save_word(fp, 0); /* store a zero byte that will be seen as a zero length method name which is invalid */
|
||||
} else {
|
||||
be_raise(vm, "internal_error", "unsupported member in class");
|
||||
return NULL; /* should never be executed */
|
||||
}
|
||||
save_proto(vm, fp, proto); /* only save prototype */
|
||||
}
|
||||
}
|
||||
return vars;
|
||||
@ -286,7 +293,7 @@ void be_bytecode_save(bvm *vm, const char *filename, bproto *proto)
|
||||
#endif /* BE_USE_BYTECODE_SAVER */
|
||||
|
||||
#if BE_USE_BYTECODE_LOADER
|
||||
static void load_proto(bvm *vm, void *fp, bproto **proto, int info, int version);
|
||||
static bbool load_proto(bvm *vm, void *fp, bproto **proto, int info, int version);
|
||||
|
||||
static uint8_t load_byte(void *fp)
|
||||
{
|
||||
@ -416,8 +423,13 @@ static void load_class(bvm *vm, void *fp, bvalue *v, int version)
|
||||
value = vm->top;
|
||||
var_setproto(value, NULL);
|
||||
be_incrtop(vm);
|
||||
load_proto(vm, fp, (bproto**)&var_toobj(value), -3, version);
|
||||
be_method_bind(vm, c, name, var_toobj(value));
|
||||
if (load_proto(vm, fp, (bproto**)&var_toobj(value), -3, version)) {
|
||||
/* actual method */
|
||||
be_method_bind(vm, c, name, var_toobj(value));
|
||||
} else {
|
||||
/* no proto, static member set to nil */
|
||||
be_member_bind(vm, c, name, bfalse);
|
||||
}
|
||||
be_stackpop(vm, 2); /* pop the cached string and proto */
|
||||
}
|
||||
for (count = 0; count < nvar; ++count) { /* load member-variable table */
|
||||
@ -509,21 +521,28 @@ static void load_upvals(bvm *vm, void *fp, bproto *proto)
|
||||
}
|
||||
}
|
||||
|
||||
static void load_proto(bvm *vm, void *fp, bproto **proto, int info, int version)
|
||||
static bbool load_proto(bvm *vm, void *fp, bproto **proto, int info, int version)
|
||||
{
|
||||
*proto = be_newproto(vm);
|
||||
(*proto)->name = load_string(vm, fp);
|
||||
(*proto)->source = load_string(vm, fp);
|
||||
(*proto)->argc = load_byte(fp);
|
||||
(*proto)->nstack = load_byte(fp);
|
||||
if (version > 1) {
|
||||
(*proto)->varg = load_byte(fp);
|
||||
load_byte(fp); /* discard reserved byte */
|
||||
/* first load the name */
|
||||
/* if empty, it's a static member so don't allocate an actual proto */
|
||||
bstring *name = load_string(vm, fp);
|
||||
if (str_len(name)) {
|
||||
*proto = be_newproto(vm);
|
||||
(*proto)->name = name;
|
||||
(*proto)->source = load_string(vm, fp);
|
||||
(*proto)->argc = load_byte(fp);
|
||||
(*proto)->nstack = load_byte(fp);
|
||||
if (version > 1) {
|
||||
(*proto)->varg = load_byte(fp);
|
||||
load_byte(fp); /* discard reserved byte */
|
||||
}
|
||||
load_bytecode(vm, fp, *proto, info);
|
||||
load_constant(vm, fp, *proto, version);
|
||||
load_proto_table(vm, fp, *proto, version);
|
||||
load_upvals(vm, fp, *proto);
|
||||
return btrue;
|
||||
}
|
||||
load_bytecode(vm, fp, *proto, info);
|
||||
load_constant(vm, fp, *proto, version);
|
||||
load_proto_table(vm, fp, *proto, version);
|
||||
load_upvals(vm, fp, *proto);
|
||||
return bfalse; /* no proto read */
|
||||
}
|
||||
|
||||
void load_global_info(bvm *vm, void *fp)
|
||||
|
@ -419,26 +419,17 @@ static void buf_add_hex(buf_impl* buf, const char *hex, size_t len)
|
||||
/********************************************************************
|
||||
** Wrapping into lib
|
||||
********************************************************************/
|
||||
// typedef int (*bntvfunc)(bvm*); /* native function pointer */
|
||||
int free_bytes_buf(bvm* vm)
|
||||
{
|
||||
int argc = be_top(vm);
|
||||
if (argc > 0) {
|
||||
buf_impl * buf = (buf_impl*) be_tocomptr(vm, 1);
|
||||
if (buf != NULL) {
|
||||
be_os_free(buf);
|
||||
}
|
||||
}
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
buf_impl * bytes_alloc(int32_t size)
|
||||
buf_impl * bytes_realloc(bvm *vm, buf_impl *oldbuf, int32_t size)
|
||||
{
|
||||
if (size < 4) { size = 4; }
|
||||
if (size > BYTES_MAX_SIZE) { size = BYTES_MAX_SIZE; }
|
||||
buf_impl * next = (buf_impl*) be_os_malloc(size + BYTES_OVERHEAD);
|
||||
size_t oldsize = oldbuf ? oldbuf->size + BYTES_OVERHEAD : 0;
|
||||
buf_impl * next = (buf_impl*) be_realloc(vm, oldbuf, oldsize, size + BYTES_OVERHEAD); /* malloc */
|
||||
next->size = size;
|
||||
next->len = 0;
|
||||
if (!oldbuf) {
|
||||
next->len = 0; /* allocate a new buffer */
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
@ -467,10 +458,10 @@ static int m_init(bvm *vm)
|
||||
} else if (argc > 1 && be_isstring(vm, 2)) {
|
||||
hex_in = be_tostring(vm, 2);
|
||||
if (hex_in) {
|
||||
size = strlen(hex_in) / 2 + BYTES_HEADROOM; // allocate headroom
|
||||
size = strlen(hex_in) / 2 + BYTES_HEADROOM; /* allocate headroom */
|
||||
}
|
||||
}
|
||||
buf_impl * buf = bytes_alloc(size);
|
||||
buf_impl * buf = bytes_realloc(vm, NULL, size); /* allocate new buffer */
|
||||
if (!buf) {
|
||||
be_throw(vm, BE_MALLOC_FAIL);
|
||||
}
|
||||
@ -478,22 +469,35 @@ static int m_init(bvm *vm)
|
||||
if (hex_in) {
|
||||
buf_add_hex(buf, hex_in, strlen(hex_in));
|
||||
}
|
||||
be_newcomobj(vm, buf, &free_bytes_buf);
|
||||
be_pushcomptr(vm, buf);
|
||||
be_setmember(vm, 1, ".p");
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
/* deallocate buffer */
|
||||
static int m_deinit(bvm *vm) {
|
||||
{
|
||||
be_getmember(vm, 1, ".p");
|
||||
buf_impl * buf = be_tocomptr(vm, -1);
|
||||
be_pop(vm, 1);
|
||||
if (buf != NULL) {
|
||||
be_realloc(vm, buf, buf->size + BYTES_OVERHEAD, 0);
|
||||
}
|
||||
be_pushcomptr(vm, NULL); /* push NULL pointer instead, just in case */
|
||||
be_setmember(vm, 1, ".p");
|
||||
be_return_nil(vm);
|
||||
}
|
||||
}
|
||||
|
||||
/* grow or shrink to the exact value */
|
||||
/* stack item 1 must contain the instance */
|
||||
static buf_impl * _bytes_resize(bvm *vm, buf_impl * buf, size_t new_size) {
|
||||
buf_impl * new_buf = bytes_alloc(new_size);
|
||||
buf_impl *new_buf = bytes_realloc(vm, buf, new_size);
|
||||
if (!new_buf) {
|
||||
be_throw(vm, BE_MALLOC_FAIL);
|
||||
}
|
||||
memmove(buf_get_buf(new_buf), buf_get_buf(buf), buf->len);
|
||||
new_buf->len = buf->len;
|
||||
/* replace the .p attribute */
|
||||
be_newcomobj(vm, new_buf, &free_bytes_buf);
|
||||
/* replace the .p attribute since address may have changed */
|
||||
be_pushcomptr(vm, new_buf);
|
||||
be_setmember(vm, 1, ".p");
|
||||
be_pop(vm, 1); /* remove comobj from stack */
|
||||
/* the old buffer will be garbage collected later */
|
||||
@ -504,7 +508,13 @@ static buf_impl * _bytes_resize(bvm *vm, buf_impl * buf, size_t new_size) {
|
||||
/* if grow, then add some headroom */
|
||||
/* stack item 1 must contain the instance */
|
||||
static buf_impl * bytes_resize(bvm *vm, buf_impl * buf, size_t new_size) {
|
||||
if (buf->size >= new_size) { return buf; } /* no resize needed */
|
||||
/* when resized to smaller, we introduce a new heurstic */
|
||||
/* If the buffer is 64 bytes or smaller, don't shrink */
|
||||
/* Shrink buffer only if target size is smaller than half the original size */
|
||||
if (buf->size >= new_size) { /* enough room, consider if need to shrink */
|
||||
if (buf->size <= 64) { return buf; } /* don't shrink if below 64 bytes */
|
||||
if (buf->size < new_size * 2) { return buf; }
|
||||
}
|
||||
return _bytes_resize(vm, buf, new_size + BYTES_HEADROOM);
|
||||
}
|
||||
|
||||
@ -1226,6 +1236,7 @@ void be_load_byteslib(bvm *vm)
|
||||
{ ".p", NULL },
|
||||
{ "_buffer", m_buffer },
|
||||
{ "init", m_init },
|
||||
{ "deinit", m_deinit },
|
||||
{ "tostring", m_tostring },
|
||||
{ "asstring", m_asstring },
|
||||
{ "fromstring", m_fromstring },
|
||||
@ -1261,6 +1272,7 @@ class be_class_bytes (scope: global, name: bytes) {
|
||||
.p, var
|
||||
_buffer, func(m_buffer)
|
||||
init, func(m_init)
|
||||
deinit, func(m_deinit)
|
||||
tostring, func(m_tostring)
|
||||
asstring, func(m_asstring)
|
||||
fromstring, func(m_fromstring)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "be_vm.h"
|
||||
#include "be_func.h"
|
||||
#include "be_var.h"
|
||||
#include <string.h>
|
||||
|
||||
#define check_members(vm, c) \
|
||||
if (!(c)->members) { \
|
||||
@ -237,6 +238,25 @@ bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
|
||||
return bfalse;
|
||||
}
|
||||
|
||||
/* Default empty constructor */
|
||||
static int default_init_native_method(bvm *vm) {
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
/* Find instance member by name and copy value to `dst` */
|
||||
/* Do not look into virtual members */
|
||||
int be_instance_member_simple(bvm *vm, binstance *instance, bstring *name, bvalue *dst)
|
||||
{
|
||||
int type;
|
||||
be_assert(name != NULL);
|
||||
binstance * obj = instance_member(vm, instance, name, dst);
|
||||
type = var_type(dst);
|
||||
if (obj && type == MT_VARIABLE) {
|
||||
*dst = obj->members[dst->v.i];
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/* Find instance member by name and copy value to `dst` */
|
||||
/* Input: none of `obj`, `name` and `dst` may not be NULL */
|
||||
/* Returns the type of the member or BE_NONE if member not found */
|
||||
@ -253,22 +273,28 @@ int be_instance_member(bvm *vm, binstance *instance, bstring *name, bvalue *dst)
|
||||
if (obj) {
|
||||
return type;
|
||||
} else { /* if no method found, try virtual */
|
||||
/* get method 'member' */
|
||||
obj = instance_member(vm, instance, str_literal(vm, "member"), vm->top);
|
||||
if (obj && basetype(var_type(vm->top)) == BE_FUNCTION) {
|
||||
bvalue *top = vm->top;
|
||||
var_setinstance(&top[1], instance);
|
||||
var_setstr(&top[2], name);
|
||||
vm->top += 3; /* prevent gc collection results */
|
||||
be_dofunc(vm, top, 2); /* call method 'member' */
|
||||
vm->top -= 3;
|
||||
*dst = *vm->top; /* copy result to R(A) */
|
||||
if (obj && var_type(dst) == MT_VARIABLE) {
|
||||
*dst = obj->members[dst->v.i];
|
||||
}
|
||||
type = var_type(dst);
|
||||
if (type != BE_NIL) {
|
||||
return type;
|
||||
/* if 'init' does not exist, create a virtual empty constructor */
|
||||
if (strcmp(str(name), "init") == 0) {
|
||||
var_setntvfunc(dst, default_init_native_method);
|
||||
return var_type(dst);
|
||||
} else {
|
||||
/* get method 'member' */
|
||||
obj = instance_member(vm, instance, str_literal(vm, "member"), vm->top);
|
||||
if (obj && basetype(var_type(vm->top)) == BE_FUNCTION) {
|
||||
bvalue *top = vm->top;
|
||||
var_setinstance(&top[1], instance);
|
||||
var_setstr(&top[2], name);
|
||||
vm->top += 3; /* prevent gc collection results */
|
||||
be_dofunc(vm, top, 2); /* call method 'member' */
|
||||
vm->top -= 3;
|
||||
*dst = *vm->top; /* copy result to R(A) */
|
||||
if (obj && var_type(dst) == MT_VARIABLE) {
|
||||
*dst = obj->members[dst->v.i];
|
||||
}
|
||||
type = var_type(dst);
|
||||
if (type != BE_NIL) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ void be_closure_method_bind(bvm *vm, bclass *c, bstring *name, bclosure *cl);
|
||||
int be_class_closure_count(bclass *c);
|
||||
void be_class_upvalue_init(bvm *vm, bclass *c);
|
||||
bbool be_class_newobj(bvm *vm, bclass *c, bvalue *argv, int argc, int mode);
|
||||
int be_instance_member_simple(bvm *vm, binstance *obj, bstring *name, bvalue *dst);
|
||||
int be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst);
|
||||
int be_class_member(bvm *vm, bclass *obj, bstring *name, bvalue *dst);
|
||||
bbool be_instance_setmember(bvm *vm, binstance *obj, bstring *name, bvalue *src);
|
||||
|
@ -111,7 +111,7 @@ void be_print_inst(binstruction ins, int pc)
|
||||
logbuf("%s\tK%d", opc2str(op), IGET_Bx(ins));
|
||||
break;
|
||||
case OP_CLOSE: case OP_LDNIL:
|
||||
logbuf("%s\t%d", opc2str(op), IGET_RA(ins));
|
||||
logbuf("%s\tR%d", opc2str(op), IGET_RA(ins));
|
||||
break;
|
||||
case OP_RAISE:
|
||||
logbuf("%s\t%d\t%c%d\t%c%d", opc2str(op), IGET_RA(ins),
|
||||
|
@ -458,10 +458,13 @@ static void destruct_object(bvm *vm, bgcobject *obj)
|
||||
int type;
|
||||
binstance *ins = cast_instance(obj);
|
||||
/* does not GC when creating the string "deinit". */
|
||||
type = be_instance_member(vm, ins, str_literal(vm, "deinit"), vm->top);
|
||||
type = be_instance_member_simple(vm, ins, str_literal(vm, "deinit"), vm->top);
|
||||
be_incrtop(vm);
|
||||
if (basetype(type) == BE_FUNCTION) {
|
||||
be_dofunc(vm, vm->top - 1, 1);
|
||||
var_setinstance(vm->top, ins); /* push instance on stack as arg 1 */
|
||||
be_incrtop(vm);
|
||||
be_dofunc(vm, vm->top - 2, 1); /* warning, there shoudln't be any exception raised here, or the gc stops */
|
||||
be_stackpop(vm, 1);
|
||||
}
|
||||
be_stackpop(vm, 1);
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ static void match_notoken(bparser *parser, btokentype type)
|
||||
}
|
||||
}
|
||||
|
||||
/* check that if the expdesc is a symbol, it is avalid one or raise an exception */
|
||||
/* check that if the expdesc is a symbol, it is a valid one or raise an exception */
|
||||
static void check_symbol(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
if (e->type == ETVOID && e->v.s == NULL) { /* error when token is not a symbol */
|
||||
@ -106,7 +106,7 @@ static void check_symbol(bparser *parser, bexpdesc *e)
|
||||
}
|
||||
}
|
||||
|
||||
/* check that the value in `e` is valid for a variable, i.e. conatins a value or a valid symbol */
|
||||
/* check that the value in `e` is valid for a variable, i.e. contains a value or a valid symbol */
|
||||
static void check_var(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
check_symbol(parser, e); /* check the token is a symbol */
|
||||
@ -792,6 +792,13 @@ static void member_expr(bparser *parser, bexpdesc *e)
|
||||
init_exp(&key, ETSTRING, 0);
|
||||
key.v.s = str;
|
||||
be_code_member(parser->finfo, e, &key);
|
||||
} else if (next_type(parser) == OptLBK) {
|
||||
scan_next_token(parser); /* skip '(' */
|
||||
bexpdesc key;
|
||||
expr(parser, &key);
|
||||
check_var(parser, &key);
|
||||
match_token(parser, OptRBK); /* skip ')' */
|
||||
be_code_member(parser->finfo, e, &key);
|
||||
} else {
|
||||
push_error(parser, "invalid syntax near '%s'",
|
||||
be_token2str(parser->vm, &next_token(parser)));
|
||||
@ -989,6 +996,7 @@ static void cond_expr(bparser *parser, bexpdesc *e)
|
||||
if (next_type(parser) == OptQuestion) {
|
||||
int jf, jl = NO_JUMP; /* jump list */
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
check_var(parser, e); /* check if valid */
|
||||
scan_next_token(parser); /* skip '?' */
|
||||
be_code_jumpbool(finfo, e, bfalse); /* go if true */
|
||||
jf = e->f;
|
||||
|
@ -267,7 +267,7 @@ bbool be_value2bool(bvm *vm, bvalue *v)
|
||||
static void obj_method(bvm *vm, bvalue *o, bstring *attr, bvalue *dst)
|
||||
{
|
||||
binstance *obj = var_toobj(o);
|
||||
int type = be_instance_member(vm, obj, attr, dst);
|
||||
int type = be_instance_member_simple(vm, obj, attr, dst);
|
||||
if (basetype(type) != BE_FUNCTION) {
|
||||
vm_error(vm, "attribute_error",
|
||||
"the '%s' object has no method '%s'",
|
||||
|
75
lib/libesp32/Berry/tests/member_indirect.be
Normal file
75
lib/libesp32/Berry/tests/member_indirect.be
Normal file
@ -0,0 +1,75 @@
|
||||
#- new syntax for indirect members -#
|
||||
|
||||
#- module accessor -#
|
||||
s_pi = 'pi'
|
||||
|
||||
import math
|
||||
assert(math.('pi') == math.pi)
|
||||
assert(math.(s_pi) == math.pi)
|
||||
|
||||
#- module writer -#
|
||||
m = module("m")
|
||||
|
||||
m.('aa') = 1
|
||||
m.('a' + 'b') = 2
|
||||
s_ac = 'ac'
|
||||
m.(s_ac) = 3
|
||||
assert(m.aa == 1)
|
||||
assert(m.ab == 2)
|
||||
assert(m.ac == 3)
|
||||
assert(m.('a'+'a') == 1)
|
||||
|
||||
#- class accessor -#
|
||||
class A1
|
||||
static a = 1, b = 2
|
||||
static s = "foo"
|
||||
def f() return 0 end
|
||||
end
|
||||
assert(A1.a == 1)
|
||||
assert(A1.b == 2)
|
||||
assert(A1.s == "foo")
|
||||
assert(type(A1.f) == 'function')
|
||||
|
||||
#- instance accessor -#
|
||||
class A2
|
||||
var a, b
|
||||
static s_a = 'a'
|
||||
def init(a,b)
|
||||
self.(self.('s_a')) = a
|
||||
self.('b') = b
|
||||
end
|
||||
def f(x)
|
||||
return x+1
|
||||
end
|
||||
def g(a,b)
|
||||
return A2(a,b)
|
||||
end
|
||||
end
|
||||
a = A2(1,2)
|
||||
|
||||
#- reading members -#
|
||||
assert(a.a == 1)
|
||||
assert(a.b == 2)
|
||||
assert(a.(A2.s_a) == 1)
|
||||
assert(a.('b') == 2)
|
||||
|
||||
#- writing members -#
|
||||
a.('a') = 10
|
||||
a.('bb'[0]) = 11
|
||||
assert(a.a == 10)
|
||||
assert(a.b == 11)
|
||||
|
||||
#- calling methods -#
|
||||
assert(a.f(1) == 2)
|
||||
assert(a.('f')(2) == 3)
|
||||
|
||||
#- mulit-level -#
|
||||
assert(a.('g')(3,4).('a') == 3)
|
||||
a.('a') = a.g(3,4)
|
||||
assert(a.a.b == 4)
|
||||
assert(a.('a').b == 4)
|
||||
assert(a.('a').('b') == 4)
|
||||
assert(a.a.('b') == 4)
|
||||
|
||||
a.('a').('b') += 1
|
||||
assert(a.a.b == 5)
|
132
lib/libesp32/Berry/tests/super_auto.be
Normal file
132
lib/libesp32/Berry/tests/super_auto.be
Normal file
@ -0,0 +1,132 @@
|
||||
#- test for new auto class inference of super() -#
|
||||
|
||||
#- test that we can call init() even if it's not defined -#
|
||||
class Z end
|
||||
z=Z()
|
||||
assert(z.init != nil)
|
||||
z.init() #- should do nothing -#
|
||||
|
||||
#- check the old way still works -#
|
||||
class A1
|
||||
var a
|
||||
def init(a)
|
||||
self.a = a
|
||||
end
|
||||
end
|
||||
class B1:A1
|
||||
var b
|
||||
def init(a,b)
|
||||
super(self,A1).init(a)
|
||||
self.b = b
|
||||
end
|
||||
end
|
||||
class C1:B1
|
||||
var c
|
||||
def init(a,b,c)
|
||||
super(self,B1).init(a,b)
|
||||
self.c = c
|
||||
end
|
||||
end
|
||||
#- -#
|
||||
c1=C1(1,2,3)
|
||||
assert(c1.a == 1)
|
||||
assert(c1.b == 2)
|
||||
assert(c1.c == 3)
|
||||
|
||||
#- test simple behavior -#
|
||||
class A0 var a end
|
||||
class B0:A0 var b end
|
||||
class C0:B0 end
|
||||
c0=C0()
|
||||
assert(classof(c0) == C0)
|
||||
assert(classof(super(c0)) == B0)
|
||||
assert(classof(super(super(c0))) == A0)
|
||||
assert(super(super(super(c0))) == nil)
|
||||
|
||||
assert(super(C0) == B0)
|
||||
assert(super(super(C0)) == A0)
|
||||
assert(super(super(super(C0))) == nil)
|
||||
|
||||
assert(classof(super(c0,B0)) == B0)
|
||||
assert(classof(super(c0,A0)) == A0)
|
||||
|
||||
#- test auto inference of target superclass -#
|
||||
class A
|
||||
var a
|
||||
def init(a)
|
||||
self.a = a
|
||||
end
|
||||
end
|
||||
class B:A
|
||||
var b
|
||||
def init(a,b)
|
||||
super(self).init(a)
|
||||
self.b = b
|
||||
end
|
||||
end
|
||||
class C:B
|
||||
var c
|
||||
def init(a,b,c)
|
||||
super(self).init(a,b)
|
||||
self.c = c
|
||||
end
|
||||
end
|
||||
#- -#
|
||||
c=C(1,2,3)
|
||||
|
||||
assert(c.a == 1)
|
||||
assert(c.b == 2)
|
||||
assert(c.c == 3)class A
|
||||
end
|
||||
class B:A
|
||||
var b
|
||||
def init(a,b) super(self).init(a) self.b = b end
|
||||
end
|
||||
class C:B
|
||||
var c
|
||||
def init(a,b,c) super(self).init(a,b) self.c = c end
|
||||
end
|
||||
c=C(1,2,3)
|
||||
|
||||
#- variant if A2 does not have an init() method, still works -#
|
||||
class A2
|
||||
static a=1
|
||||
end
|
||||
class B2:A2
|
||||
var b
|
||||
def init(a,b) super(self).init(a) self.b = b end
|
||||
end
|
||||
class C2:B2
|
||||
var c
|
||||
def init(a,b,c) super(self).init(a,b) self.c = c end
|
||||
end
|
||||
#- -#
|
||||
c2=C2(1,2,3)
|
||||
assert(c2.a == 1)
|
||||
assert(c2.b == 2)
|
||||
assert(c2.c == 3)
|
||||
|
||||
#- difference in behavior whether the second arg is provided or not -#
|
||||
class A3
|
||||
end
|
||||
class B3:A3
|
||||
def b1()
|
||||
return super(self)
|
||||
end
|
||||
def b2(c)
|
||||
return super(self, c)
|
||||
end
|
||||
end
|
||||
class C3:B3
|
||||
end
|
||||
#- -#
|
||||
b3=B3()
|
||||
c3=C3()
|
||||
assert(classof(c3.b1()) == A3)
|
||||
assert(classof(b3.b1()) == A3)
|
||||
assert(classof(c3.b2(B3)) == B3)
|
||||
assert(classof(c3.b2(A3)) == A3)
|
||||
|
||||
assert(classof(c3.b2(nil)) == B3) #- testing super(self<C3>,nil) in B3::b2() -#
|
||||
|
||||
assert(c3.b2(C3) == nil) #- if specifying the current class, can't find any relevant class in supers -#
|
@ -26,10 +26,9 @@ assert(classname(super(super(C))) == 'A')
|
||||
assert(super(super(super(C))) == nil)
|
||||
|
||||
#- super() levele -#
|
||||
assert(super(a,A) == a)
|
||||
assert(classname(super(a,A)) == 'A')
|
||||
assert(classname(super(b,B)) == 'B')
|
||||
assert(classname(super(c,C)) == 'C')
|
||||
assert(super(a,A) == nil)
|
||||
assert(super(b,B) == nil)
|
||||
assert(super(c,C) == nil)
|
||||
assert(classname(super(c,B)) == 'B')
|
||||
assert(classname(super(c,A)) == 'A')
|
||||
assert(super(c,map) == nil) #- not a parent class -#
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -835,6 +835,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -28,7 +28,7 @@
|
||||
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
|
||||
* Use online command Prefix to translate cmnd, stat and tele.
|
||||
*
|
||||
* Updated until v9.5.0.3
|
||||
* Updated until v9.5.0.7
|
||||
\*********************************************************************/
|
||||
|
||||
//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
|
||||
@ -234,8 +234,8 @@
|
||||
#define D_SYSLOG_HOST_NOT_FOUND "Syslog-Host nicht gefunden"
|
||||
|
||||
// settings.ino
|
||||
#define D_SAVED_TO_FLASH_AT "in Flash gespeichert am"
|
||||
#define D_LOADED_FROM_FLASH_AT "aus Flash geladen am"
|
||||
#define D_SAVED_TO_FLASH_AT "in Flash gespeichert an"
|
||||
#define D_LOADED_FROM_FLASH_AT "aus Flash geladen von"
|
||||
#define D_USE_DEFAULTS "Standard verwenden"
|
||||
#define D_ERASED_SECTOR "gelöschter Sektor"
|
||||
|
||||
@ -330,7 +330,7 @@
|
||||
#define D_MQTT_TLS_ENABLE "MQTT TLS"
|
||||
#define D_HTTP_API "HTTP API"
|
||||
#define D_HTTP_API_ENABLE "HTTP API aktivieren"
|
||||
#define D_FRIENDLY_NAME "Name [friendly name]"
|
||||
#define D_FRIENDLY_NAME "Name [Friendly Name]"
|
||||
#define D_BELKIN_WEMO "Belkin WeMo"
|
||||
#define D_HUE_BRIDGE "Hue Bridge"
|
||||
#define D_SINGLE_DEVICE "Einzelnes Gerät"
|
||||
@ -493,19 +493,19 @@
|
||||
#define D_ZIGBEE_UNKNWON_ATTRIBUTE "Unbekannter Attribut Name (ignoriert): %s"
|
||||
#define D_ZIGBEE_TOO_MANY_CLUSTERS "Nur eine Cluster id pro Kommando"
|
||||
#define D_ZIGBEE_WRONG_DELIMITER "Falscher Delimeter für Payload"
|
||||
#define D_ZIGBEE_UNRECOGNIZED_COMMAND "Unerkanntes zigbee Kommando: %s"
|
||||
#define D_ZIGBEE_UNRECOGNIZED_COMMAND "Unerkanntes Zigbee Kommando: %s"
|
||||
#define D_ZIGBEE_TOO_MANY_COMMANDS "Nur 1 Kommando zulässig (%d)"
|
||||
#define D_ZIGBEE_NO_ATTRIBUTE "Kein Attribut in der Liste"
|
||||
#define D_ZIGBEE_UNSUPPORTED_ATTRIBUTE_TYPE "Nicht unterstützter Attribut Typ"
|
||||
#define D_ZIGBEE_JSON_REQUIRED "Konfiguration muss JSON basiert sein"
|
||||
#define D_ZIGBEE_RESET_1_OR_2 "1 oder 2 für Reset"
|
||||
#define D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS "ZBBridge EEPROM gefunden auf Adresse"
|
||||
#define D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS "ZBBridge EEPROM gefunden an Adresse"
|
||||
#define D_ZIGBEE_RANDOMIZING_ZBCONFIG "Zufällige Zigbee Parameter erstellt, Überprüfung mit 'ZbConfig'"
|
||||
|
||||
// xdrv_03_energy.ino
|
||||
#define D_ENERGY_TODAY "Energie heute"
|
||||
#define D_ENERGY_YESTERDAY "Energie gestern"
|
||||
#define D_ENERGY_TOTAL "Energie insgesamt"
|
||||
#define D_ENERGY_TOTAL "Energie gesamt"
|
||||
|
||||
// xdrv_27_shutter.ino
|
||||
#define D_OPEN "Öffnen"
|
||||
@ -533,7 +533,7 @@
|
||||
#define D_CHECKSUM_FAILURE "Prüfsummen-Fehler"
|
||||
|
||||
// xsns_07_sht1x.ino
|
||||
#define D_SENSOR_DID_NOT_ACK_COMMAND "Sensor hat ACK-Befehl nicht ausgeführt"
|
||||
#define D_SENSOR_DID_NOT_ACK_COMMAND "Sensor hat Befehl nicht ausgeführt"
|
||||
#define D_SHT1X_FOUND "SHT1X gefunden"
|
||||
|
||||
// xsns_18_pms5003.ino
|
||||
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
@ -942,9 +945,9 @@
|
||||
#define D_FS_SIZE "Größe"
|
||||
#define D_FS_FREE "Frei"
|
||||
#define D_NEW_FILE "neue-datei.txt"
|
||||
#define D_CREATE_NEW_FILE "Neue Datei erstellen und bearbeiten"
|
||||
#define D_CREATE_NEW_FILE "Datei erstellen und bearbeiten"
|
||||
#define D_EDIT_FILE "Datei bearbeiten"
|
||||
#define D_CONFIRM_FILE_DEL "Löschen der Datei bestätigen"
|
||||
#define D_CONFIRM_FILE_DEL "Datei löschen bestätigen"
|
||||
|
||||
//xsns_67_as3935.ino
|
||||
#define D_AS3935_GAIN "Umgebung:"
|
||||
@ -1014,7 +1017,7 @@
|
||||
#define D_FP_INVALIDIMAGE "Abbild ungültig" // 0x15 Failed to generate image because of lac of valid primary image
|
||||
#define D_FP_FLASHERR "Flash Schreibfehler" // 0x18 Error when writing flash
|
||||
#define D_FP_INVALIDREG "Ungültige ID-Nummer" // 0x1A Invalid register number
|
||||
#define D_FP_ADDRCODE "Addresse" // 0x20 Address code
|
||||
#define D_FP_ADDRCODE "Adresse" // 0x20 Address code
|
||||
#define D_FP_PASSVERIFY "Übereinstimmung" // 0x21 Verify the fingerprint passed
|
||||
#define D_FP_UNKNOWNERROR "Fehler" // Any other error
|
||||
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "Effetto hall"
|
||||
#define D_SENSOR_EPD_DATA "EPD - Dati"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 - CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "Efekt Halla"
|
||||
#define D_SENSOR_EPD_DATA "EPD Dane"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "Efeito Hall"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "Efeito Hall"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -836,6 +836,9 @@
|
||||
#define D_SENSOR_HALLEFFECT "HallEffect"
|
||||
#define D_SENSOR_EPD_DATA "EPD Data"
|
||||
#define D_SENSOR_MCP2515_CS "MCP2515 CS"
|
||||
#define D_SENSOR_HRG15_RX "HRG15 Rx"
|
||||
#define D_SENSOR_HRG15_TX "HRG15 Tx"
|
||||
#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "安培"
|
||||
|
@ -743,6 +743,8 @@
|
||||
//#define USE_AS608 // Add support for AS608 optical and R503 capacitive fingerprint sensor (+3k code)
|
||||
// #define USE_AS608_MESSAGES // Add verbose error messages (+0k4 code)
|
||||
//#define USE_TFMINIPLUS // Add support for TFmini Plus (TFmini, TFmini-S) LiDAR modules via UART interface (+0k8)
|
||||
//#define USE_HRG15 // Add support for Hydreon RG-15 Solid State Rain sensor (+1k5 code)
|
||||
//#define USE_VINDRIKTNING // Add support for IKEA VINDRIKTNING particle concentration sensor (+1k code)
|
||||
|
||||
// -- Power monitoring sensors --------------------
|
||||
#define USE_ENERGY_SENSOR // Add support for Energy Monitors (+14k code)
|
||||
|
@ -762,9 +762,12 @@ void ResponseAppendFeatures(void)
|
||||
#ifdef USE_INFLUXDB
|
||||
feature8 |= 0x00000800; // xdrv_59_influxdb.ino
|
||||
#endif
|
||||
|
||||
// feature8 |= 0x00001000;
|
||||
// feature8 |= 0x00002000;
|
||||
#ifdef USE_HRG15
|
||||
feature8 |= 0x00001000; // xsns_90_hrg15.ino
|
||||
#endif
|
||||
#ifdef USE_VINDRIKTNING
|
||||
feature8 |= 0x00002000; // xsns_91_vindriktning.ino
|
||||
#endif
|
||||
// feature8 |= 0x00004000;
|
||||
// feature8 |= 0x00008000;
|
||||
|
||||
|
@ -237,20 +237,32 @@ void SwitchHandler(uint32_t mode) {
|
||||
uint32_t mqtt_action = POWER_NONE;
|
||||
uint32_t switchmode = Settings->switchmode[i];
|
||||
|
||||
if (Switch.hold_timer[i] & (((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK)) {
|
||||
bool push_hold_multi_delay = ((PUSHHOLDMULTIDELAY == switchmode) || (PUSHHOLDMULTIDELAY_INV == switchmode));
|
||||
if (push_hold_multi_delay) {
|
||||
switchmode -= (PUSHHOLDMULTIDELAY - PUSHHOLDMULTI);
|
||||
}
|
||||
bool push_hold_multi = ((PUSHHOLDMULTI == switchmode) || (PUSHHOLDMULTI_INV == switchmode));
|
||||
|
||||
if (Switch.hold_timer[i] & ((push_hold_multi) ? SM_TIMER_MASK : SM_NO_TIMER_MASK)) {
|
||||
Switch.hold_timer[i]--;
|
||||
if ((Switch.hold_timer[i] & SM_TIMER_MASK) == loops_per_second * Settings->param[P_HOLD_TIME] / 25) {
|
||||
if ((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)){
|
||||
if (((switchmode == PUSHHOLDMULTI) & (NOT_PRESSED == Switch.last_state[i])) | ((switchmode == PUSHHOLDMULTI_INV) & (PRESSED == Switch.last_state[i]))) {
|
||||
SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT
|
||||
}
|
||||
else if ((Settings->flag5.switch_dimmer_act_at_rls) & ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS)) {
|
||||
switchflag = POWER_TOGGLE; // Toggle with pushbutton
|
||||
Switch.hold_timer[i] = 0;
|
||||
}
|
||||
bool do_sendkey = false;
|
||||
switch (switchmode) {
|
||||
case PUSHHOLDMULTI:
|
||||
do_sendkey = (NOT_PRESSED == Switch.last_state[i]);
|
||||
break;
|
||||
case PUSHHOLDMULTI_INV:
|
||||
do_sendkey = (PRESSED == Switch.last_state[i]);
|
||||
break;
|
||||
}
|
||||
if (do_sendkey) {
|
||||
SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT
|
||||
} else if (push_hold_multi_delay && ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS)) {
|
||||
switchflag = POWER_TOGGLE; // Toggle with pushbutton
|
||||
Switch.hold_timer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (0 == (Switch.hold_timer[i] & (((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK))) {
|
||||
if (0 == (Switch.hold_timer[i] & ((push_hold_multi) ? SM_TIMER_MASK: SM_NO_TIMER_MASK))) {
|
||||
switch (switchmode) {
|
||||
case TOGGLEMULTI:
|
||||
switchflag = POWER_TOGGLE; // Toggle after hold
|
||||
@ -277,7 +289,6 @@ void SwitchHandler(uint32_t mode) {
|
||||
Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 25;
|
||||
SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT
|
||||
mqtt_action = POWER_INCREMENT;
|
||||
|
||||
} else {
|
||||
Switch.hold_timer[i]= 0;
|
||||
SendKey(KEY_SWITCH, i +1, POWER_CLEAR); // Execute command via MQTT
|
||||
@ -352,10 +363,10 @@ void SwitchHandler(uint32_t mode) {
|
||||
}
|
||||
} else {
|
||||
if ((Switch.hold_timer[i] & SM_TIMER_MASK) > loops_per_second * Settings->param[P_HOLD_TIME] / 25) {
|
||||
if((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) {
|
||||
if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) {
|
||||
Switch.hold_timer[i]= SM_FIRST_PRESS;
|
||||
if (!Settings->flag5.switch_dimmer_act_at_rls){
|
||||
switchflag = POWER_TOGGLE; // Toggle with pushbutton
|
||||
if (!push_hold_multi_delay) {
|
||||
switchflag = POWER_TOGGLE; // Toggle with pushbutton
|
||||
}
|
||||
}
|
||||
else{
|
||||
@ -380,10 +391,10 @@ void SwitchHandler(uint32_t mode) {
|
||||
}
|
||||
} else {
|
||||
if ((Switch.hold_timer[i] & SM_TIMER_MASK)> loops_per_second * Settings->param[P_HOLD_TIME] / 25) {
|
||||
if((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) {
|
||||
if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) {
|
||||
Switch.hold_timer[i]= SM_FIRST_PRESS;
|
||||
if (!Settings->flag5.switch_dimmer_act_at_rls){
|
||||
switchflag = POWER_TOGGLE; // Toggle with pushbutton
|
||||
if (!push_hold_multi_delay) {
|
||||
switchflag = POWER_TOGGLE; // Toggle with pushbutton
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
@ -280,7 +280,8 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D
|
||||
enum WifiConfigOptions {WIFI_RESTART, EX_WIFI_SMARTCONFIG, WIFI_MANAGER, EX_WIFI_WPSCONFIG, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY, MAX_WIFI_OPTION};
|
||||
|
||||
enum SwitchModeOptions {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, TOGGLEMULTI,
|
||||
FOLLOWMULTI, FOLLOWMULTI_INV, PUSHHOLDMULTI, PUSHHOLDMULTI_INV, PUSHON, PUSHON_INV, PUSH_IGNORE, MAX_SWITCH_OPTION};
|
||||
FOLLOWMULTI, FOLLOWMULTI_INV, PUSHHOLDMULTI, PUSHHOLDMULTI_INV, PUSHON, PUSHON_INV, PUSH_IGNORE, PUSHNOTUSED, PUSHHOLDMULTIDELAY,
|
||||
PUSHHOLDMULTIDELAY_INV, MAX_SWITCH_OPTION};
|
||||
|
||||
enum LedStateOptions {LED_OFF, LED_POWER, LED_MQTTSUB, LED_POWER_MQTTSUB, LED_MQTTPUB, LED_POWER_MQTTPUB, LED_MQTT, LED_POWER_MQTT, MAX_LED_OPTION};
|
||||
|
||||
|
@ -188,6 +188,7 @@
|
||||
// #define USE_PROJECTOR_CTRL_OPTOMA // Use codes for OPTOMA
|
||||
//#define USE_AS608 // Add support for AS608 optical and R503 capacitive fingerprint sensor (+3k4 code)
|
||||
//#define USE_TFMINIPLUS // Add suppoer for TFmini Plus (TFmini, TFmini-S) LiDAR modules via UART interface
|
||||
//#define USE_HRG15 // Add support for Hydreon RG-15 Solid State Rain sensor (+1k5 code)
|
||||
|
||||
#define USE_ENERGY_SENSOR // Add energy sensors (-14k code)
|
||||
#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
|
||||
|
@ -476,6 +476,7 @@
|
||||
// #define USE_PROJECTOR_CTRL_OPTOMA // Use codes for OPTOMA
|
||||
//#define USE_AS608 // Add support for AS608 optical and R503 capacitive fingerprint sensor (+3k4 code)
|
||||
//#define USE_TFMINIPLUS // Add support for TFmini Plus (TFmini, TFmini-S) LiDAR modules via UART interface
|
||||
//#define USE_HRG15 // Add support for Hydreon RG-15 Solid State Rain sensor (+1k5 code)
|
||||
|
||||
#define USE_ENERGY_SENSOR // Add energy sensors (-14k code)
|
||||
#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
|
||||
|
@ -170,6 +170,8 @@ enum UserSelectablePins {
|
||||
GPIO_I2S_IN_DATA, GPIO_I2S_IN_CLK, GPIO_I2S_IN_SLCT,
|
||||
GPIO_INTERRUPT,
|
||||
GPIO_MCP2515_CS, // MCP2515 Chip Select
|
||||
GPIO_HRG15_TX, GPIO_HRG15_RX, // Hydreon RG-15 rain sensor serial interface
|
||||
GPIO_VINDRIKTNING_RX, // IKEA VINDRIKTNING Serial interface
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
enum ProgramSelectablePins {
|
||||
@ -360,6 +362,8 @@ const char kSensorNames[] PROGMEM =
|
||||
D_SENSOR_I2S_IN_DATA "|" D_SENSOR_I2S_IN_CLK "|" D_SENSOR_I2S_IN_SLCT "|"
|
||||
D_SENSOR_INTERRUPT "|"
|
||||
D_SENSOR_MCP2515_CS "|"
|
||||
D_SENSOR_HRG15_TX "|" D_SENSOR_HRG15_RX "|"
|
||||
D_SENSOR_VINDRIKTNING_RX
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
@ -791,6 +795,13 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
||||
AGPIO(GPIO_AS608_TX),
|
||||
AGPIO(GPIO_AS608_RX),
|
||||
#endif
|
||||
#ifdef USE_HRG15
|
||||
AGPIO(GPIO_HRG15_TX),
|
||||
AGPIO(GPIO_HRG15_RX),
|
||||
#endif
|
||||
#ifdef USE_VINDRIKTNING
|
||||
AGPIO(GPIO_VINDRIKTNING_RX),
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*\
|
||||
* Other sensors
|
||||
|
@ -1171,7 +1171,11 @@ void CmndSubscribe(void)
|
||||
subscription_item.Key = key;
|
||||
subscriptions.add(subscription_item);
|
||||
|
||||
if (2 == XdrvMailbox.index) {
|
||||
topic = subscription_item.Topic; // Do not append "/#""
|
||||
}
|
||||
MqttSubscribe(topic.c_str());
|
||||
|
||||
events.concat(event_name + "," + topic
|
||||
+ (key.length()>0 ? "," : "")
|
||||
+ key);
|
||||
|
@ -182,6 +182,11 @@ bool SnfL1SerialInput(void) {
|
||||
snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer);
|
||||
}
|
||||
|
||||
else if (!strncmp(token2, "\"mode\"", 6)) {
|
||||
uint8_t received_mode = atoi(token3);
|
||||
Settings->sbflag1.sonoff_l1_music_sync = (SONOFF_L1_MODE_SYNC_TO_MUSIC == received_mode);
|
||||
}
|
||||
|
||||
token = strtok_r(nullptr, ",", &end_str);
|
||||
}
|
||||
|
||||
@ -250,19 +255,19 @@ bool SnfL1SetChannels(void) {
|
||||
}
|
||||
if (!power_changed && !dimmer_changed && !color_changed && (Snfl1.old_music_sync == Settings->sbflag1.sonoff_l1_music_sync)) { return true; }
|
||||
|
||||
uint32_t mode = SONOFF_L1_MODE_COLORFUL;
|
||||
if (Settings->sbflag1.sonoff_l1_music_sync) {
|
||||
mode = SONOFF_L1_MODE_SYNC_TO_MUSIC;
|
||||
}
|
||||
|
||||
snprintf_P(Snfl1.buffer, SONOFF_L1_BUFFER_SIZE, PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d,\"sensitive\":%d,\"speed\":%d"),
|
||||
uint32_t mode = (Settings->sbflag1.sonoff_l1_music_sync) ? SONOFF_L1_MODE_SYNC_TO_MUSIC : SONOFF_L1_MODE_COLORFUL;
|
||||
snprintf_P(Snfl1.buffer, SONOFF_L1_BUFFER_SIZE, PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"),
|
||||
LocalTime(), millis()%1000,
|
||||
Snfl1.power ? "on" : "off",
|
||||
Snfl1.color[0], Snfl1.color[1], Snfl1.color[2],
|
||||
Snfl1.dimmer,
|
||||
mode,
|
||||
Snfl1.sensitive,
|
||||
Snfl1.speed);
|
||||
mode);
|
||||
if (SONOFF_L1_MODE_SYNC_TO_MUSIC == mode) {
|
||||
snprintf_P(Snfl1.buffer, SONOFF_L1_BUFFER_SIZE, PSTR("%s,\"sensitive\":%d,\"speed\":%d"),
|
||||
Snfl1.buffer,
|
||||
Snfl1.sensitive,
|
||||
Snfl1.speed);
|
||||
}
|
||||
|
||||
#ifdef SONOFF_L1_START_DELAY
|
||||
static bool first_call = true;
|
||||
@ -281,6 +286,16 @@ bool SnfL1SetChannels(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SnfL1SetChannelsFromFunc(void) {
|
||||
static bool first_call = true;
|
||||
if (first_call) {
|
||||
first_call = false; // Allow MusicSync at init time
|
||||
} else {
|
||||
Settings->sbflag1.sonoff_l1_music_sync = 0; // Disable MusicSync on user color change
|
||||
}
|
||||
return SnfL1SetChannels();
|
||||
}
|
||||
|
||||
bool SnfL1ModuleSelected(void) {
|
||||
if (SONOFF_L1 == TasmotaGlobal.module_type) {
|
||||
if (PinUsed(GPIO_RXD) && PinUsed(GPIO_TXD)) {
|
||||
@ -340,7 +355,7 @@ bool Xlgt05(uint8_t function)
|
||||
result = SnfL1SerialInput();
|
||||
break;
|
||||
case FUNC_SET_CHANNELS:
|
||||
result = SnfL1SetChannels();
|
||||
result = SnfL1SetChannelsFromFunc();
|
||||
break;
|
||||
case FUNC_MODULE_INIT:
|
||||
result = SnfL1ModuleSelected();
|
||||
|
@ -48,6 +48,7 @@ struct PMS5003 {
|
||||
uint8_t valid = 0;
|
||||
uint8_t wake_mode = 1;
|
||||
uint8_t ready = 1;
|
||||
bool discovery_triggered = false;
|
||||
} Pms;
|
||||
|
||||
enum PmsCommands
|
||||
@ -156,6 +157,11 @@ bool PmsReadData(void)
|
||||
#endif // PMS_MODEL_PMS3003
|
||||
Pms.valid = 10;
|
||||
|
||||
if (!Pms.discovery_triggered) {
|
||||
TasmotaGlobal.discovery_counter = 1; // Force discovery
|
||||
Pms.discovery_triggered = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
233
tasmota/xsns_90_hrg15.ino
Normal file
233
tasmota/xsns_90_hrg15.ino
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
xsns_90-hrg15.ino - Hydreon RG-15 support for Tasmota
|
||||
|
||||
Copyright (c) 2021 Wouter Breukink
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_HRG15
|
||||
/*********************************************************************************************\
|
||||
* Hydreon RG-15
|
||||
* See https://rainsensors.com/products/rg-15/
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_90 90
|
||||
|
||||
#define RG15_NAME "RG-15"
|
||||
#define RG15_BAUDRATE 9600
|
||||
#define RG15_READ_TIMEOUT 500
|
||||
#define RG15_EVENT_TIMEOUT 60
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
const char HTTP_RG15[] PROGMEM =
|
||||
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
"{s}" RG15_NAME " " D_JSON_ACTIVE "{m}%2_f " D_UNIT_MILLIMETER "{e}"
|
||||
"{s}" RG15_NAME " " D_JSON_EVENT "{m}%2_f " D_UNIT_MILLIMETER "{e}"
|
||||
"{s}" RG15_NAME " " D_JSON_TOTAL "{m}%2_f " D_UNIT_MILLIMETER "{e}"
|
||||
"{s}" RG15_NAME " " D_JSON_FLOWRATE "{m}%2_f " D_UNIT_MILLIMETER "/" D_UNIT_HOUR "{e}";
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
TasmotaSerial *HydreonSerial;
|
||||
|
||||
struct RG15 {
|
||||
uint16_t time = RG15_EVENT_TIMEOUT;
|
||||
uint8_t ready = 1;
|
||||
uint8_t received = 0;
|
||||
float acc = 0.0f;
|
||||
float event = 0.0f;
|
||||
float total = 0.0f;
|
||||
float rate = 0.0f;
|
||||
} Rg15;
|
||||
|
||||
void Rg15Init(void)
|
||||
{
|
||||
Rg15.ready = 0;
|
||||
if (PinUsed(GPIO_HRG15_RX) && PinUsed(GPIO_HRG15_TX)) {
|
||||
HydreonSerial = new TasmotaSerial(Pin(GPIO_HRG15_RX), Pin(GPIO_HRG15_TX));
|
||||
if (HydreonSerial->begin(RG15_BAUDRATE)) {
|
||||
if (HydreonSerial->hardwareSerial()) { ClaimSerial(); }
|
||||
|
||||
HydreonSerial->println('R');
|
||||
|
||||
Rg15.ready = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Rg15Poll(void) {
|
||||
|
||||
// Trigger the first update
|
||||
if (! Rg15.received) {
|
||||
HydreonSerial->println('R');
|
||||
}
|
||||
|
||||
if (! HydreonSerial->available()) {
|
||||
|
||||
// Check if the rain event has timed out, reset rate to 0
|
||||
if (++Rg15.time == RG15_EVENT_TIMEOUT) {
|
||||
Rg15.acc = 0;
|
||||
Rg15.rate = 0;
|
||||
MqttPublishSensor();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now read what's available
|
||||
char rg15_buffer[255];
|
||||
|
||||
while (HydreonSerial->available()) {
|
||||
Rg15ReadLine(rg15_buffer);
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("%s:" D_JSON_SERIALRECEIVED " = %s"),"HRG", rg15_buffer);
|
||||
|
||||
Rg15Process(rg15_buffer);
|
||||
}
|
||||
|
||||
MqttPublishSensor();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rg15ReadLine(char* buffer)
|
||||
{
|
||||
char c;
|
||||
uint8_t i = 0;
|
||||
uint32_t cmillis = millis();
|
||||
|
||||
while (1) {
|
||||
if (HydreonSerial->available()) {
|
||||
c = HydreonSerial->read();
|
||||
buffer[i++] = c;
|
||||
|
||||
if (c == 10) { break; } // New line ends the message
|
||||
if (i == 254) { break; } // Overflow
|
||||
}
|
||||
|
||||
if ((millis() - cmillis) > RG15_READ_TIMEOUT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
buffer[i-2] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
void Rg15Process(char* buffer) {
|
||||
|
||||
// Process payload, example: Acc 0.01 mm, EventAcc 2.07 mm, TotalAcc 54.85 mm, RInt 2.89 mmph
|
||||
Rg15.received = 1;
|
||||
Rg15.acc = Rg15Parse(buffer, "Acc");
|
||||
Rg15.event = Rg15Parse(buffer, "EventAcc");
|
||||
Rg15.total = Rg15Parse(buffer, "TotalAcc");
|
||||
Rg15.rate = Rg15Parse(buffer, "RInt");
|
||||
|
||||
if (Rg15.acc > 0.0f) {
|
||||
Rg15.time = 0; // We have some data, so the rain event is on-going
|
||||
}
|
||||
}
|
||||
|
||||
float Rg15Parse(char* buffer, const char* item) {
|
||||
char* start = strstr(buffer, item);
|
||||
if (start != nullptr) {
|
||||
char* end = strstr(start, " mm");
|
||||
char tmp = end[0];
|
||||
end[0] = '\0';
|
||||
float result = CharToFloat (start + strlen(item));
|
||||
end[0] = tmp;
|
||||
return result;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool Rg15Command(void) {
|
||||
bool serviced = true;
|
||||
|
||||
if (XdrvMailbox.data_len == 1) {
|
||||
char *send = XdrvMailbox.data;
|
||||
HydreonSerial->println(send);
|
||||
HydreonSerial->flush();
|
||||
|
||||
if (send[0] == 'k' || send[0] == 'K' || send[0] == 'o' || send[0] == 'O') {
|
||||
ResponseCmndDone();
|
||||
return serviced;
|
||||
}
|
||||
|
||||
char rg15_buffer[255];
|
||||
if (Rg15ReadLine(rg15_buffer)) {
|
||||
Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":%s\"}"), rg15_buffer);
|
||||
Rg15Process(rg15_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return serviced;
|
||||
}
|
||||
|
||||
void Rg15Show(bool json)
|
||||
{
|
||||
if (!Rg15.received) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"" RG15_NAME "\":{\"" D_JSON_ACTIVE "\":%2_f, \"" D_JSON_EVENT "\":%2_f, \"" D_JSON_TOTAL "\":%2_f, \"" D_JSON_FLOWRATE "\":%2_f}"), &Rg15.acc, &Rg15.event, &Rg15.total, &Rg15.rate);
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_RG15, &Rg15.acc, &Rg15.event, &Rg15.total, &Rg15.rate);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns90(uint8_t function)
|
||||
{
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (Rg15.ready)
|
||||
{
|
||||
switch (function)
|
||||
{
|
||||
case FUNC_INIT:
|
||||
Rg15Init();
|
||||
break;
|
||||
case FUNC_COMMAND_SENSOR:
|
||||
if (XSNS_90 == XdrvMailbox.index) {
|
||||
Rg15Command();
|
||||
}
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
Rg15Poll();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
Rg15Show(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
Rg15Show(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_HRG15
|
170
tasmota/xsns_91_vindriktning.ino
Normal file
170
tasmota/xsns_91_vindriktning.ino
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
xsns_91_vindriktning.ino - IKEA vindriktning particle concentration sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2021 Marcel Ritter and Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_VINDRIKTNING
|
||||
/*********************************************************************************************\
|
||||
* IKEA VINDRIKTNING particle concentration sensor
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_91 91
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
#ifndef MIN_INTERVAL_PERIOD
|
||||
#define MIN_INTERVAL_PERIOD 60 // minimum interval period in seconds required for passive mode
|
||||
#endif
|
||||
|
||||
#define VINDRIKTNING_DATASET_SIZE 20
|
||||
|
||||
TasmotaSerial *VindriktningSerial;
|
||||
|
||||
struct VINDRIKTNING {
|
||||
uint16_t pm2_5 = 0;
|
||||
uint16_t pm1_0 = 0;
|
||||
uint16_t pm10 = 0;
|
||||
uint8_t type = 1;
|
||||
uint8_t valid = 0;
|
||||
bool discovery_triggered = false;
|
||||
} Vindriktning;
|
||||
|
||||
bool VindriktningReadData(void) {
|
||||
if (!VindriktningSerial->available()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int serial_in_byte_counter = 0;
|
||||
uint8_t buffer[VINDRIKTNING_DATASET_SIZE];
|
||||
uint8_t crc = 0;
|
||||
|
||||
while (VindriktningSerial->available()) {
|
||||
uint8_t serial_in_byte = VindriktningSerial->read();
|
||||
if (serial_in_byte_counter <= VINDRIKTNING_DATASET_SIZE -1) {
|
||||
buffer[serial_in_byte_counter++] = serial_in_byte;
|
||||
crc += serial_in_byte;
|
||||
}
|
||||
}
|
||||
VindriktningSerial->flush(); // Make room for another burst
|
||||
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, VINDRIKTNING_DATASET_SIZE);
|
||||
|
||||
if (serial_in_byte_counter < VINDRIKTNING_DATASET_SIZE) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("VDN: Not enough data (%d < 20)"), serial_in_byte_counter);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (crc != 0) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("VDN: " D_CHECKSUM_FAILURE));
|
||||
return false;
|
||||
}
|
||||
|
||||
// sample data:
|
||||
// 16 11 0b 00 00 00 0c 00 00 03 cb 00 00 00 0c 01 00 00 00 e7
|
||||
// |pm2_5| |pm1_0| |pm10 | | CRC |
|
||||
Vindriktning.pm2_5 = (buffer[5] << 8) | buffer[6];
|
||||
Vindriktning.pm1_0 = (buffer[9] << 8) | buffer[10];
|
||||
Vindriktning.pm10 = (buffer[13] << 8) | buffer[14];
|
||||
|
||||
if (!Vindriktning.discovery_triggered) {
|
||||
TasmotaGlobal.discovery_counter = 1; // force TasDiscovery()
|
||||
Vindriktning.discovery_triggered = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void VindriktningSecond(void) { // Every second
|
||||
if (VindriktningReadData()) {
|
||||
Vindriktning.valid = MIN_INTERVAL_PERIOD;
|
||||
} else {
|
||||
if (Vindriktning.valid) {
|
||||
Vindriktning.valid--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void VindriktningInit(void) {
|
||||
Vindriktning.type = 0;
|
||||
if (PinUsed(GPIO_VINDRIKTNING_RX)) {
|
||||
VindriktningSerial = new TasmotaSerial(Pin(GPIO_VINDRIKTNING_RX), -1, 1);
|
||||
if (VindriktningSerial->begin(9600)) {
|
||||
if (VindriktningSerial->hardwareSerial()) { ClaimSerial(); }
|
||||
Vindriktning.type = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
const char HTTP_VINDRIKTNING_SNS[] PROGMEM =
|
||||
"{s}VINDRIKTNING " D_ENVIRONMENTAL_CONCENTRATION " 1.0 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
|
||||
"{s}VINDRIKTNING " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
|
||||
"{s}VINDRIKTNING " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
void VindriktningShow(bool json) {
|
||||
if (Vindriktning.valid) {
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"VINDRIKTNING\":{\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d}"),
|
||||
Vindriktning.pm1_0, Vindriktning.pm2_5, Vindriktning.pm10);
|
||||
#ifdef USE_DOMOTICZ
|
||||
if (0 == TasmotaGlobal.tele_period) {
|
||||
DomoticzSensor(DZ_COUNT, Vindriktning.pm1_0); // PM1.0
|
||||
DomoticzSensor(DZ_VOLTAGE, Vindriktning.pm2_5); // PM2.5
|
||||
DomoticzSensor(DZ_CURRENT, Vindriktning.pm10); // PM10
|
||||
}
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_VINDRIKTNING_SNS, Vindriktning.pm1_0, Vindriktning.pm2_5, Vindriktning.pm10);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns91(uint8_t function) {
|
||||
bool result = false;
|
||||
|
||||
if (Vindriktning.type) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
VindriktningSecond();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
VindriktningShow(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
VindriktningShow(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
case FUNC_INIT:
|
||||
VindriktningInit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_VINDRIKTNING
|
@ -256,7 +256,7 @@ a_features = [[
|
||||
"USE_MPU_ACCEL","USE_TFMINIPLUS","USE_CSE7761","USE_BERRY",
|
||||
"USE_BM8563","USE_ENERGY_DUMMY","USE_AM2320","USE_T67XX",
|
||||
"USE_MCP2515","USE_TASMESH","USE_WIFI_RANGE_EXTENDER","USE_INFLUXDB",
|
||||
"","","","",
|
||||
"USE_HRG15","USE_VINDRIKTNING","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
@ -288,7 +288,7 @@ else:
|
||||
obj = json.load(fp)
|
||||
|
||||
def StartDecode():
|
||||
print ("\n*** decode-status.py v20210812 by Theo Arends and Jacek Ziolkowski ***")
|
||||
print ("\n*** decode-status.py v20210826 by Theo Arends and Jacek Ziolkowski ***")
|
||||
|
||||
# print("Decoding\n{}".format(obj))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user