Merge remote-tracking branch 'upstream/development' into DimmerNewState

This commit is contained in:
Seestern93 2021-08-29 22:41:34 +02:00
commit 535900f9f3
59 changed files with 1082 additions and 161 deletions

View File

@ -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'

View File

@ -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'

View File

@ -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``

View File

@ -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)

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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(

View File

@ -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);
}

View File

@ -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)

View File

@ -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)

View File

@ -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;
}
}
}
}

View File

@ -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);

View File

@ -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),

View File

@ -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);
}

View File

@ -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;

View File

@ -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'",

View 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)

View 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 -#

View File

@ -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 -#

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 "А"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 "А"

View File

@ -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"

View File

@ -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"

View File

@ -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 "安培"

View File

@ -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)

View File

@ -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;

View File

@ -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{

View File

@ -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};

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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
View 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

View 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

View File

@ -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))