Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2402 lines
68 KiB

8 years ago
8 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
6 years ago
6 years ago
8 years ago
6 years ago
8 years ago
8 years ago
8 years ago
6 years ago
8 years ago
6 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
7 years ago
6 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
8 years ago
8 years ago
6 years ago
6 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
6 years ago
6 years ago
6 years ago
6 years ago
providers: relays, lights and buttons refactoring (#2414) - gpio module now tracks the known providers (right now, hardware and mcp expander) - refactored relay struct to use 'Provider' implementing setup,notify,change,boot instead of just BasePin actions - refactored button module to use gpio provider instead of referencing types itself - removed dual & stm code from buttons, migrate both to relay module - added status notify and change callbacks for relayStatus (i.e. 'notify' when relay status was called, but not changed. and 'changed' when it did) - relays runtime configuration keys - relay command now shows configured relays and current & target statuses - refactor the code using relayStatus(0, blah) under LIGHT_PROVIDER check to use lightState instead - remove rfbridge code form relay module. implement through a basic state listener in the rfbridge module, depend on RELAY_SUPPORT - allow to bind rf codes to real relays - drop tuya-specific lights provider, remove tuya code from relays and lights modules - integrate tuya via relay listeners and providers, use lights custom provider - implement channel transitions for tuya. disabled by default, and transition time and step are overridden to 2000 + 100. needs to be set to some value below the total time (i.e. `total transition time / step time == number of steps`, we need to figure out a correct time that serial comms could handle) - lights custom provider (global, not per-pin) and state listeners - remove lights code from relay module. implement through providers & listeners in the lights module, depend on RELAY_SUPPORT - lights per-channel relay provider (unused atm), depend on RELAY_SUPPORT - refactored channel transition - calculate step only once, make sure time + step values are sane, generate quick transitions with very small delay (10ms hardcoded) for transitions during OFF state i.e. we no longer waste 500ms (or whatever transition time is set to) on boot doing nothing - transition time + step parameter for the lightUpdate - report mask parameter for the lightUpdate - minor fixes across the board resolve #2222
3 years ago
6 years ago
6 years ago
6 years ago
8 years ago
6 years ago
8 years ago
6 years ago
8 years ago
8 years ago
8 years ago
8 years ago
6 years ago
8 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
6 years ago
6 years ago
6 years ago
8 years ago
  1. var debug = false;
  2. var websock;
  3. var password = false;
  4. var maxNetworks;
  5. var free_size = 0;
  6. var urls = {};
  7. var numChanged = 0;
  8. var numReboot = 0;
  9. var numReconnect = 0;
  10. var numReload = 0;
  11. var configurationSaved = false;
  12. var ws_pingpong;
  13. //removeIf(!light)
  14. var colorPicker;
  15. var useWhite = false;
  16. var useCCT = false;
  17. //endRemoveIf(!light)
  18. var now = 0;
  19. var ago = 0;
  20. //removeIf(!rfm69)
  21. var packets;
  22. var filters = [];
  23. //endRemoveIf(!rfm69)
  24. //removeIf(!sensor)
  25. var Magnitudes = [];
  26. var MagnitudeErrors = {};
  27. var MagnitudeNames = {};
  28. var MagnitudeTypePrefixes = {};
  29. var MagnitudePrefixTypes = {};
  30. //endRemoveIf(!sensor)
  31. // -----------------------------------------------------------------------------
  32. // Utils
  33. // -----------------------------------------------------------------------------
  34. $.fn.enterKey = function (fnc) {
  35. return this.each(function () {
  36. $(this).keypress(function (ev) {
  37. var keycode = parseInt(ev.keyCode ? ev.keyCode : ev.which, 10);
  38. if (13 === keycode) {
  39. return fnc.call(this, ev);
  40. }
  41. });
  42. });
  43. };
  44. function followScroll(id, threshold) {
  45. if (threshold === undefined) {
  46. threshold = 90;
  47. }
  48. var elem = document.getElementById(id);
  49. var offset = (elem.scrollTop + elem.offsetHeight) / elem.scrollHeight * 100;
  50. if (offset > threshold) {
  51. elem.scrollTop = elem.scrollHeight;
  52. }
  53. }
  54. function fromSchema(source, schema) {
  55. if (schema.length !== source.length) {
  56. throw "Schema mismatch!";
  57. }
  58. var target = {};
  59. schema.forEach(function(key, index) {
  60. target[key] = source[index];
  61. });
  62. return target;
  63. }
  64. function keepTime() {
  65. $("span[name='ago']").html(ago);
  66. ago++;
  67. if (0 === now) { return; }
  68. var date = new Date(now * 1000);
  69. var text = date.toISOString().substring(0, 19).replace("T", " ");
  70. $("input[name='now']").val(text);
  71. $("span[name='now']").html(text);
  72. now++;
  73. }
  74. function zeroPad(number, positions) {
  75. return number.toString().padStart(positions, "0");
  76. }
  77. function validatePassword(password) {
  78. // http://www.the-art-of-web.com/javascript/validate-password/
  79. // at least one lowercase and one uppercase letter or number
  80. // at least eight characters (letters, numbers or special characters)
  81. // MUST be 8..63 printable ASCII characters. See:
  82. // https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access#Target_users_(authentication_key_distribution)
  83. // https://github.com/xoseperez/espurna/issues/1151
  84. var re_password = /^(?=.*[A-Z\d])(?=.*[a-z])[\w~!@#$%^&*\(\)<>,.\?;:{}\[\]\\|]{8,63}$/;
  85. return (
  86. (password !== undefined)
  87. && (typeof password === "string")
  88. && (password.length > 0)
  89. && re_password.test(password)
  90. );
  91. }
  92. function validateFormPasswords(form) {
  93. var passwords = $("input[name='adminPass0'],input[name='adminPass1']", form);
  94. var adminPass1 = passwords.first().val(),
  95. adminPass2 = passwords.last().val();
  96. var formValidity = passwords.first()[0].checkValidity();
  97. if (formValidity && (adminPass1.length === 0) && (adminPass2.length === 0)) {
  98. return true;
  99. }
  100. var validPass1 = validatePassword(adminPass1),
  101. validPass2 = validatePassword(adminPass2);
  102. if (formValidity && validPass1 && validPass2) {
  103. return true;
  104. }
  105. if (!formValidity || (adminPass1.length > 0 && !validPass1)) {
  106. alert("The password you have entered is not valid, it must be 8..63 characters and have at least 1 lowercase and 1 uppercase / number!");
  107. }
  108. if (adminPass1 !== adminPass2) {
  109. alert("Passwords are different!");
  110. }
  111. return false;
  112. }
  113. function validateFormHostname(form) {
  114. // RFCs mandate that a hostname's labels may contain only
  115. // the ASCII letters 'a' through 'z' (case-insensitive),
  116. // the digits '0' through '9', and the hyphen.
  117. // Hostname labels cannot begin or end with a hyphen.
  118. // No other symbols, punctuation characters, or blank spaces are permitted.
  119. // Negative lookbehind does not work in Javascript
  120. // var re_hostname = new RegExp('^(?!-)[A-Za-z0-9-]{1,32}(?<!-)$');
  121. var re_hostname = new RegExp('^(?!-)[A-Za-z0-9-]{0,31}[A-Za-z0-9]$');
  122. var hostname = $("input[name='hostname']", form);
  123. if ("true" !== hostname.attr("hasChanged")) {
  124. return true;
  125. }
  126. if (re_hostname.test(hostname.val())) {
  127. return true;
  128. }
  129. alert("Hostname cannot be empty and may only contain the ASCII letters ('A' through 'Z' and 'a' through 'z'), the digits '0' through '9', and the hyphen ('-')! They can neither start or end with an hyphen.");
  130. return false;
  131. }
  132. function validateForm(form) {
  133. return validateFormPasswords(form) && validateFormHostname(form);
  134. }
  135. // Observe all group settings to selectively update originals based on the current data
  136. var groupSettingsObserver = new MutationObserver(function(mutations) {
  137. mutations.forEach(function(mutation) {
  138. // If any new elements are added, set "settings-target" element as changed to forcibly send the data
  139. var targets = $(mutation.target).attr("data-settings-target");
  140. if (targets !== undefined) {
  141. mutation.addedNodes.forEach(function(node) {
  142. var overrides = [];
  143. targets.split(" ").forEach(function(target) {
  144. var elem = $("[name='" + target + "']", node);
  145. if (!elem.length) return;
  146. var value = getValue(elem);
  147. if ((value === null) || (value === elem[0].defaultValue)) {
  148. overrides.push(elem);
  149. }
  150. });
  151. setOriginalsFromValues($("input,select", node));
  152. overrides.forEach(function(elem) {
  153. elem.attr("hasChanged", "true");
  154. if (elem.prop("tagName") === "SELECT") {
  155. elem.prop("value", 0);
  156. }
  157. });
  158. });
  159. }
  160. // If anything was removed, forcibly send **all** of the group to avoid having any outdated keys
  161. // TODO: hide instead of remove?
  162. var changed = $(mutation.target).attr("hasChanged") === "true";
  163. if (changed || mutation.removedNodes.length) {
  164. $(mutation.target).attr("hasChanged", "true");
  165. $("input,select", mutation.target.childNodes).attr("hasChanged", "true");
  166. }
  167. });
  168. });
  169. function bitsetToValues(bitset) {
  170. var values = [];
  171. for (var index = 0; index < 31; ++index) {
  172. if (bitset & (1 << index)) {
  173. values.push(String(index));
  174. }
  175. }
  176. return values;
  177. }
  178. function valuesToBitset(values) {
  179. var result = 0;
  180. for (var value of values) {
  181. result |= 1 << parseInt(value);
  182. }
  183. return result;
  184. }
  185. function getValue(element) {
  186. if ($(element).attr("type") === "checkbox") {
  187. return $(element).prop("checked") ? 1 : 0;
  188. } else if ($(element).attr("type") === "radio") {
  189. if (!$(element).prop("checked")) {
  190. return null;
  191. }
  192. } else if ($(element).attr("multiple") !== undefined) {
  193. return valuesToBitset($(element).val());
  194. }
  195. return $(element).val();
  196. }
  197. function getData(form, changed, cleanup) {
  198. // Populate two sets of data, ones that had been changed and ones that stayed the same
  199. var data = {};
  200. var changed_data = [];
  201. if (cleanup === undefined) {
  202. cleanup = true;
  203. }
  204. if (changed === undefined) {
  205. changed = true;
  206. }
  207. $("input,select", form).each(function() {
  208. if ($(this).attr("data-settings-ignore") === "true") {
  209. return;
  210. }
  211. var name = $(this).attr("name");
  212. if (name === undefined) {
  213. return;
  214. }
  215. var real_name = $(this).attr("data-settings-real-name");
  216. if (real_name !== undefined) {
  217. name = real_name;
  218. }
  219. var value = getValue(this);
  220. if (null !== value) {
  221. var haschanged = ("true" === $(this).attr("hasChanged"));
  222. var indexed = changed_data.indexOf(name) >= 0;
  223. if ((haschanged || !changed) && !indexed) {
  224. changed_data.push(name);
  225. }
  226. // make sure to group keys from templates (or, manually flagged as such)
  227. var is_group = $(this).attr("data-settings-group") !== undefined;
  228. if (is_group) {
  229. if (name in data) {
  230. data[name].push(value);
  231. } else {
  232. data[name] = [value];
  233. }
  234. } else {
  235. data[name] = value;
  236. }
  237. }
  238. });
  239. // Finally, filter out only fields that had changed.
  240. // Note: We need to preserve dynamic lists like schedules, wifi etc.
  241. // so we don't accidentally break when user deletes entry in the middle
  242. var resulting_data = {};
  243. for (var value in data) {
  244. if (changed_data.indexOf(value) >= 0) {
  245. resulting_data[value] = data[value];
  246. }
  247. }
  248. // Hack: clean-up leftover arrays.
  249. // When empty, the receiving side will prune all keys greater than the current one.
  250. if (cleanup) {
  251. $(".group-settings").each(function() {
  252. var haschanged = ("true" === $(this).attr("hasChanged"));
  253. if (haschanged && !this.children.length) {
  254. var targets = this.dataset.settingsTarget;
  255. if (targets === undefined) return;
  256. targets.split(" ").forEach(function(target) {
  257. resulting_data[target] = [];
  258. });
  259. }
  260. });
  261. }
  262. return resulting_data;
  263. }
  264. function randomString(length, args) {
  265. if (typeof args === "undefined") {
  266. args = {
  267. lowercase: true,
  268. uppercase: true,
  269. numbers: true,
  270. special: true
  271. }
  272. }
  273. var mask = "";
  274. if (args.lowercase) { mask += "abcdefghijklmnopqrstuvwxyz"; }
  275. if (args.uppercase) { mask += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
  276. if (args.numbers || args.hex) { mask += "0123456789"; }
  277. if (args.hex) { mask += "ABCDEF"; }
  278. if (args.special) { mask += "~`!@#$%^&*()_+-={}[]:\";'<>?,./|\\"; }
  279. var source = new Uint32Array(length);
  280. var result = new Array(length);
  281. window.crypto.getRandomValues(source).forEach(function(value, i) {
  282. result[i] = mask[value % mask.length];
  283. });
  284. return result.join("");
  285. }
  286. function generateAPIKey() {
  287. var apikey = randomString(16, {hex: true});
  288. $("input[name='apiKey']")
  289. .val(apikey)
  290. .attr("original", "-".repeat(16))
  291. .attr("haschanged", "true");
  292. return false;
  293. }
  294. function generatePassword() {
  295. var password = "";
  296. do {
  297. password = randomString(10);
  298. } while (!validatePassword(password));
  299. return password;
  300. }
  301. function toggleVisiblePassword() {
  302. var elem = this.previousElementSibling;
  303. if (elem.type === "password") {
  304. elem.type = "text";
  305. } else {
  306. elem.type = "password";
  307. }
  308. return false;
  309. }
  310. function doGeneratePassword() {
  311. var elems = $("input", $("#formPassword"));
  312. elems
  313. .val(generatePassword())
  314. .attr("haschanged", "true")
  315. .each(function() {
  316. this.type = "text";
  317. });
  318. return false;
  319. }
  320. function moduleVisible(module) {
  321. if (module == "sch") {
  322. $("li.module-" + module).css("display", "inherit");
  323. $("div.module-" + module).css("display", "flex");
  324. return;
  325. }
  326. $(".module-" + module).css("display", "inherit");
  327. }
  328. //removeIf(!thermostat)
  329. function checkTempRangeMin() {
  330. var min = parseInt($("#tempRangeMinInput").val(), 10);
  331. var max = parseInt($("#tempRangeMaxInput").val(), 10);
  332. if (min > max - 1) {
  333. $("#tempRangeMinInput").val(max - 1);
  334. }
  335. }
  336. function checkTempRangeMax() {
  337. var min = parseInt($("#tempRangeMinInput").val(), 10);
  338. var max = parseInt($("#tempRangeMaxInput").val(), 10);
  339. if (max < min + 1) {
  340. $("#tempRangeMaxInput").val(min + 1);
  341. }
  342. }
  343. function doResetThermostatCounters(ask) {
  344. var question = (typeof ask === "undefined" || false === ask) ?
  345. null :
  346. "Are you sure you want to reset burning counters?";
  347. return doAction(question, "thermostat_reset_counters");
  348. }
  349. //endRemoveIf(!thermostat)
  350. function initSelectGPIO(select) {
  351. // TODO: properly lock used GPIOs via locking and apply the mask here
  352. var mapping = [
  353. [153, "NONE"],
  354. [0, "0 (FLASH)"],
  355. [1, "1 (U0TXD)"],
  356. [2, "2 (U1TXD)"],
  357. [3, "3 (U0RXD)"],
  358. [4, "4 (SDA)"],
  359. [5, "5 (SCL)"],
  360. [9, "9 (SDD2)"],
  361. [10, "10 (SDD3)"],
  362. [12, "12 (MTDI)"],
  363. [13, "13 (MTCK)"],
  364. [14, "14 (MTMS)"],
  365. [15, "15 (MTDO)"],
  366. [16, "16 (WAKE)"],
  367. ];
  368. for (n in mapping) {
  369. var elem = $('<option value="' + mapping[n][0] + '">');
  370. elem.html(mapping[n][1]);
  371. elem.appendTo(select);
  372. }
  373. }
  374. // -----------------------------------------------------------------------------
  375. // Actions
  376. // -----------------------------------------------------------------------------
  377. function send(json) {
  378. if (debug) console.log(json);
  379. websock.send(json);
  380. }
  381. function sendAction(action, data) {
  382. send(JSON.stringify({action: action, data: data}));
  383. }
  384. function sendConfig(data) {
  385. send(JSON.stringify({config: data}));
  386. }
  387. function setOriginalsFromValues(elems) {
  388. if (typeof elems == "undefined") {
  389. elems = $("input,select");
  390. }
  391. elems.each(function() {
  392. var value;
  393. if ($(this).attr("type") === "checkbox") {
  394. value = $(this).prop("checked");
  395. } else {
  396. value = $(this).val();
  397. }
  398. $(this).attr("original", value);
  399. hasChanged.call(this);
  400. });
  401. }
  402. function resetOriginals() {
  403. setOriginalsFromValues();
  404. $(".group-settings").attr("haschanged", "false")
  405. numReboot = numReconnect = numReload = 0;
  406. configurationSaved = false;
  407. }
  408. function doReload(milliseconds) {
  409. setTimeout(function() {
  410. window.location.reload();
  411. }, parseInt(milliseconds, 10));
  412. }
  413. /**
  414. * Check a file object to see if it is a valid firmware image
  415. * The file first byte should be 0xE9
  416. * @param {file} file File object
  417. * @param {Function} callback Function to call back with the result
  418. */
  419. function checkFirmware(file, callback) {
  420. var reader = new FileReader();
  421. reader.onloadend = function(evt) {
  422. if (FileReader.DONE === evt.target.readyState) {
  423. var magic = evt.target.result.charCodeAt(0);
  424. if ((0x1F === magic) && (0x8B === evt.target.result.charCodeAt(1))) {
  425. callback(true);
  426. return;
  427. }
  428. if (0xE9 !== magic) {
  429. alert("Binary image does not start with a magic byte");
  430. callback(false);
  431. return;
  432. }
  433. var modes = ['QIO', 'QOUT', 'DIO', 'DOUT'];
  434. var flash_mode = evt.target.result.charCodeAt(2);
  435. if (0x03 !== flash_mode) {
  436. var response = window.confirm("Binary image is using " + modes[flash_mode] + " flash mode! Make sure that the device supports it before proceeding.");
  437. callback(response);
  438. } else {
  439. callback(true);
  440. }
  441. }
  442. };
  443. var blob = file.slice(0, 3);
  444. reader.readAsBinaryString(blob);
  445. }
  446. function doUpgrade() {
  447. var file = $("input[name='upgrade']")[0].files[0];
  448. if (typeof file === "undefined") {
  449. alert("First you have to select a file from your computer.");
  450. return false;
  451. }
  452. if (file.size > free_size) {
  453. alert("Image it too large to fit in the available space for OTA. Consider doing a two-step update.");
  454. return false;
  455. }
  456. checkFirmware(file, function(ok) {
  457. if (!ok) {
  458. return;
  459. }
  460. var data = new FormData();
  461. data.append("upgrade", file, file.name);
  462. var xhr = new XMLHttpRequest();
  463. var msg_ok = "Firmware image uploaded, board rebooting. This page will be refreshed in 5 seconds.";
  464. var msg_err = "There was an error trying to upload the new image, please try again: ";
  465. var network_error = function(e) {
  466. alert(msg_err + " xhr request " + e.type);
  467. };
  468. xhr.addEventListener("error", network_error, false);
  469. xhr.addEventListener("abort", network_error, false);
  470. xhr.addEventListener("load", function(e) {
  471. $("#upgrade-progress").hide();
  472. if ("OK" === xhr.responseText) {
  473. alert(msg_ok);
  474. doReload(5000);
  475. } else {
  476. alert(msg_err + xhr.status.toString() + " " + xhr.statusText + ", " + xhr.responseText);
  477. }
  478. }, false);
  479. xhr.upload.addEventListener("progress", function(e) {
  480. $("#upgrade-progress").show();
  481. if (e.lengthComputable) {
  482. $("progress").attr({ value: e.loaded, max: e.total });
  483. }
  484. }, false);
  485. xhr.open("POST", urls.upgrade.href);
  486. xhr.send(data);
  487. });
  488. return false;
  489. }
  490. function doUpdatePassword() {
  491. var form = $("#formPassword");
  492. if (validateFormPasswords(form)) {
  493. sendConfig(getData(form, true, false));
  494. }
  495. return false;
  496. }
  497. function checkChanges() {
  498. if (numChanged > 0) {
  499. var response = window.confirm("Some changes have not been saved yet, do you want to save them first?");
  500. if (response) {
  501. doUpdate();
  502. }
  503. }
  504. }
  505. function doAction(question, action) {
  506. checkChanges();
  507. if (question) {
  508. var response = window.confirm(question);
  509. if (false === response) {
  510. return false;
  511. }
  512. }
  513. sendAction(action, {});
  514. doReload(5000);
  515. return false;
  516. }
  517. function doReboot(ask) {
  518. var question = (typeof ask === "undefined" || false === ask) ?
  519. null :
  520. "Are you sure you want to reboot the device?";
  521. return doAction(question, "reboot");
  522. }
  523. function doReconnect(ask) {
  524. var question = (typeof ask === "undefined" || false === ask) ?
  525. null :
  526. "Are you sure you want to disconnect from the current WIFI network?";
  527. return doAction(question, "reconnect");
  528. }
  529. function doCheckOriginals() {
  530. var response;
  531. if (numReboot > 0) {
  532. response = window.confirm("You have to reboot the board for the changes to take effect, do you want to do it now?");
  533. if (response) { doReboot(false); }
  534. } else if (numReconnect > 0) {
  535. response = window.confirm("You have to reconnect to the WiFi for the changes to take effect, do you want to do it now?");
  536. if (response) { doReconnect(false); }
  537. } else if (numReload > 0) {
  538. response = window.confirm("You have to reload the page to see the latest changes, do you want to do it now?");
  539. if (response) { doReload(0); }
  540. }
  541. resetOriginals();
  542. }
  543. function waitForSave(){
  544. if (!configurationSaved) {
  545. setTimeout(waitForSave, 1000);
  546. } else {
  547. doCheckOriginals();
  548. }
  549. }
  550. function doUpdate() {
  551. // Since we have 2-page config, make sure we select the active one
  552. var forms = $(".form-settings");
  553. if (validateForm(forms)) {
  554. sendConfig(getData(forms));
  555. //removeIf(!sensor)
  556. // Energy reset is handled via these keys
  557. // TODO: replace these with actions, not settings
  558. $(".pwrExpected").val(0);
  559. $("input[name='snsResetCalibration']").prop("checked", false);
  560. $("input[name='pwrResetCalibration']").prop("checked", false);
  561. $("input[name='pwrResetE']").prop("checked", false);
  562. //endRemoveIf(!sensor)
  563. numChanged = 0;
  564. waitForSave();
  565. }
  566. return false;
  567. }
  568. function doBackup() {
  569. document.getElementById("downloader").src = urls.config.href;
  570. return false;
  571. }
  572. function onFileUpload(event) {
  573. var inputFiles = this.files;
  574. if (typeof inputFiles === "undefined" || inputFiles.length === 0) {
  575. return false;
  576. }
  577. var inputFile = inputFiles[0];
  578. this.value = "";
  579. var response = window.confirm("Previous settings will be overwritten. Are you sure you want to restore this settings?");
  580. if (!response) {
  581. return false;
  582. }
  583. var reader = new FileReader();
  584. reader.onload = function(e) {
  585. try {
  586. var data = JSON.parse(e.target.result);
  587. sendAction("restore", data);
  588. } catch (e) {
  589. window.alert(e);
  590. }
  591. };
  592. reader.readAsText(inputFile);
  593. return false;
  594. }
  595. function doRestore() {
  596. if (typeof window.FileReader !== "function") {
  597. alert("The file API isn't supported on this browser yet.");
  598. } else {
  599. $("#uploader").click();
  600. }
  601. return false;
  602. }
  603. function doFactoryReset() {
  604. var response = window.confirm("Are you sure you want to restore to factory settings?");
  605. if (!response) {
  606. return false;
  607. }
  608. sendAction("factory_reset", {});
  609. doReload(5000);
  610. return false;
  611. }
  612. function doToggle(id, value) {
  613. sendAction("relay", {id: id, status: value ? 1 : 0 });
  614. return false;
  615. }
  616. function doScan() {
  617. $("#scanResult").html("");
  618. $("div.scan.loading").show();
  619. $("#button-wifi-scan").attr("disabled", true);
  620. sendAction("scan", {});
  621. return false;
  622. }
  623. function doDebugCommand() {
  624. var el = $("input[name='dbgcmd']");
  625. var command = el.val();
  626. el.val("");
  627. sendAction("dbgcmd", {command: command});
  628. followScroll("weblog", 0);
  629. return false;
  630. }
  631. function doDebugClear() {
  632. $("#weblog").text("");
  633. return false;
  634. }
  635. //removeIf(!rfm69)
  636. function doClearCounts() {
  637. sendAction("clear-counts", {});
  638. return false;
  639. }
  640. function doClearMessages() {
  641. packets.clear().draw(false);
  642. return false;
  643. }
  644. function doFilter(e) {
  645. var index = packets.cell(this).index();
  646. if (index == 'undefined') return;
  647. var c = index.column;
  648. var column = packets.column(c);
  649. if (filters[c]) {
  650. filters[c] = false;
  651. column.search("");
  652. $(column.header()).removeClass("filtered");
  653. } else {
  654. filters[c] = true;
  655. var data = packets.row(this).data();
  656. if (e.which == 1) {
  657. column.search('^' + data[c] + '$', true, false );
  658. } else {
  659. column.search('^((?!(' + data[c] + ')).)*$', true, false );
  660. }
  661. $(column.header()).addClass("filtered");
  662. }
  663. column.draw();
  664. return false;
  665. }
  666. function doClearFilters() {
  667. for (var i = 0; i < packets.columns()[0].length; i++) {
  668. if (filters[i]) {
  669. filters[i] = false;
  670. var column = packets.column(i);
  671. column.search("");
  672. $(column.header()).removeClass("filtered");
  673. column.draw();
  674. }
  675. }
  676. return false;
  677. }
  678. //endRemoveIf(!rfm69)
  679. function delParent() {
  680. var parent = $(this).parent().parent();
  681. $(parent).remove();
  682. }
  683. // -----------------------------------------------------------------------------
  684. // Visualization
  685. // -----------------------------------------------------------------------------
  686. function toggleMenu() {
  687. $("#layout").toggleClass("active");
  688. $("#menu").toggleClass("active");
  689. $("#menuLink").toggleClass("active");
  690. }
  691. function showPanel() {
  692. $(".panel").hide();
  693. if ($("#layout").hasClass("active")) { toggleMenu(); }
  694. $("#" + $(this).attr("data")).show();
  695. }
  696. function loadTemplate(name) {
  697. let template = $(`#${name}.template`);
  698. let clone = $(template).clone();
  699. return clone.children();
  700. }
  701. function loadConfigTemplate(name) {
  702. let template = loadTemplate(name);
  703. $("input,select", template)
  704. .attr("data-settings-group", "");
  705. return template;
  706. }
  707. // -----------------------------------------------------------------------------
  708. // Relays & magnitudes mapping
  709. // -----------------------------------------------------------------------------
  710. function createRelayList(data, container, template_name) {
  711. var current = $("#" + container + " > div").length;
  712. if (current > 0) { return; }
  713. var template = loadConfigTemplate(template_name);
  714. for (var i in data) {
  715. var line = $(template).clone();
  716. $("label", line).html("Switch #" + i);
  717. setOriginalsFromValues($("input", line));
  718. line.appendTo("#" + container);
  719. }
  720. }
  721. //removeIf(!sensor)
  722. function createMagnitudeList(data, container, template_name) {
  723. var current = $("#" + container + " > div").length;
  724. if (current > 0) { return; }
  725. var template = loadConfigTemplate(template_name);
  726. var size = data.size;
  727. for (var i=0; i<size; ++i) {
  728. var line = $(template).clone();
  729. $("label", line).html(MagnitudeNames[data.type[i]] + " #" + parseInt(data.index[i], 10));
  730. $("div.hint", line).html(Magnitudes[i].description);
  731. setOriginalsFromValues($("input", line));
  732. line.appendTo("#" + container);
  733. }
  734. }
  735. //endRemoveIf(!sensor)
  736. function addFromTemplate(template, target) {
  737. var line = loadConfigTemplate(template);
  738. $("button", line)
  739. .on('click', delParent);
  740. setOriginalsFromValues($("input", line));
  741. line.appendTo("#" + target);
  742. }
  743. // -----------------------------------------------------------------------------
  744. // RPN Rules
  745. // -----------------------------------------------------------------------------
  746. function addRPNRule() {
  747. addFromTemplate("rpnRuleTemplate", "rpnRules");
  748. }
  749. function addRPNTopic() {
  750. addFromTemplate("rpnRuleTemplate", "rpnTopics");
  751. }
  752. // -----------------------------------------------------------------------------
  753. // RFM69
  754. // -----------------------------------------------------------------------------
  755. //removeIf(!rfm69)
  756. function addMapping() {
  757. addFromTemplate("nodeTemplate", "mapping");
  758. }
  759. //endRemoveIf(!rfm69)
  760. // -----------------------------------------------------------------------------
  761. // Wifi
  762. // -----------------------------------------------------------------------------
  763. function numNetworks() {
  764. return $("#networks > div").length;
  765. }
  766. function delNetwork() {
  767. var parent = $(this).parents(".pure-g");
  768. $(parent).remove();
  769. }
  770. function moreNetwork() {
  771. var parent = $(this).parents(".pure-g");
  772. $(".more", parent).toggle();
  773. }
  774. function addNetwork(network) {
  775. var number = numNetworks();
  776. if (number >= maxNetworks) {
  777. alert("Max number of networks reached");
  778. return null;
  779. }
  780. if (network === undefined) {
  781. network = {};
  782. }
  783. var line = loadConfigTemplate("networkTemplate");
  784. $(".password-reveal", line).on("click", toggleVisiblePassword);
  785. $(line).find(".button-del-network").on("click", delNetwork);
  786. $(line).find(".button-more-network").on("click", moreNetwork);
  787. Object.entries(network).forEach(function(pair) {
  788. // XXX: UI deleting this network will only re-use stored values.
  789. var key = pair[0],
  790. val = pair[1];
  791. if (key === "stored") {
  792. $(line).find(".button-del-network").prop("disabled", val);
  793. return;
  794. }
  795. $("input[name='" + key + "']", line).val(val);
  796. });
  797. line.appendTo("#networks");
  798. return line;
  799. }
  800. // -----------------------------------------------------------------------------
  801. // Relays scheduler
  802. // -----------------------------------------------------------------------------
  803. function numSchedules() {
  804. return $("#schedules > div").length;
  805. }
  806. function maxSchedules() {
  807. var value = $("#schedules").attr("data-settings-max");
  808. return parseInt(value === undefined ? 0 : value, 10);
  809. }
  810. function delSchedule() {
  811. var parent = $(this).parents(".pure-g");
  812. $(parent).remove();
  813. }
  814. function moreSchedule() {
  815. var parent = $(this).parents(".pure-g");
  816. $("div.more", parent).toggle();
  817. }
  818. function addSchedule(values) {
  819. var schedules = numSchedules();
  820. if (schedules >= maxSchedules()) {
  821. alert("Max number of schedules reached");
  822. return null;
  823. }
  824. if (values === undefined) {
  825. values = {};
  826. }
  827. var line = loadConfigTemplate("scheduleTemplate");
  828. var type = "none";
  829. switch(values.schType) {
  830. case 1:
  831. type = "switch";
  832. break;
  833. case 2:
  834. type = "light";
  835. break;
  836. case 3:
  837. type = "curtain";
  838. break;
  839. }
  840. var actions = $("#" + type + "ActionTemplate").children();
  841. $(line).find("#schActionDiv").append(actions.clone());
  842. $(line).find(".button-del-schedule").on("click", delSchedule);
  843. $(line).find(".button-more-schedule").on("click", moreSchedule);
  844. var schUTC_id = "schUTC" + schedules;
  845. $(line).find("input[name='schUTC']").prop("id", schUTC_id).next().prop("for", schUTC_id);
  846. var schEnabled_id = "schEnabled" + schedules;
  847. $(line).find("input[name='schEnabled']").prop("id", schEnabled_id).next().prop("for", schEnabled_id);
  848. $(line).find("input[type='checkbox']").prop("checked", false);
  849. Object.entries(values).forEach(function(kv) {
  850. var key = kv[0], value = kv[1];
  851. $("input[name='" + key + "']", line).val(value);
  852. $("select[name='" + key + "']", line).prop("value", value);
  853. $("input[type='checkbox'][name='" + key + "']", line).prop("checked", value);
  854. });
  855. line.appendTo("#schedules");
  856. return line;
  857. }
  858. // -----------------------------------------------------------------------------
  859. // Relays
  860. // -----------------------------------------------------------------------------
  861. function initRelayFromSchema(id, relay, schema) {
  862. var result = fromSchema(relay, schema)
  863. if (!result.name.length) {
  864. result.name = "Switch #" + id;
  865. }
  866. return result;
  867. }
  868. function initRelays(data) {
  869. var current = $("#relays > div").length;
  870. if (current > 0) { return; }
  871. var schema = data.schema;
  872. data["relays"].forEach(function(relay, id) {
  873. var _relay = initRelayFromSchema(id, relay, schema);
  874. var line = loadConfigTemplate("relayTemplate");
  875. $("span.relay-name", line)
  876. .text(_relay.name)
  877. .attr("data-id", id);
  878. $("input[type='checkbox']", line)
  879. .prop('checked', false)
  880. .prop('disabled', true)
  881. .attr("data-id", id)
  882. .prop("id", "relay" + id)
  883. .on("change", function (event) {
  884. var target= parseInt($(event.target).attr("data-id"), 10);
  885. var status = $(event.target).prop("checked");
  886. doToggle(target, status);
  887. });
  888. $("label.toggle", line)
  889. .prop("for", "relay" + id);
  890. line.appendTo("#relays");
  891. });
  892. }
  893. function updateRelays(data) {
  894. var size = data.size;
  895. for (var i=0; i<size; ++i) {
  896. var elem = $("input[name='relay'][data='" + i + "']");
  897. elem.prop("checked", data.status[i]);
  898. var lock = {
  899. 0: false,
  900. 1: !data.status[i],
  901. 2: data.status[i]
  902. };
  903. elem.prop("disabled", lock[data.lock[i]]); // RELAY_LOCK_DISABLED=0
  904. }
  905. }
  906. function createCheckboxes() {
  907. $("input[type='checkbox']").each(function() {
  908. let name = $(this).prop("name")
  909. if ($(this).prop("name")) {
  910. $(this).prop("id", $(this).prop("name"));
  911. }
  912. $(this)
  913. .parent().addClass("toggleWrapper");
  914. $(this)
  915. .after('<label for="' + $(this).prop("name") + '" class="toggle"><span class="toggle__handler"></span></label>')
  916. });
  917. }
  918. function initRelayConfig(data) {
  919. var current = $("#relayConfig > legend").length; // there is a legend per relay
  920. if (current > 0) { return; }
  921. var schema = data.schema;
  922. data["relays"].forEach(function(relay, id) {
  923. var _relay = initRelayFromSchema(id, relay, schema);
  924. var line = loadConfigTemplate("relayConfigTemplate");
  925. $("span.name", line).html(_relay.name);
  926. $("span.prov", line).html(_relay.prov);
  927. $("select[name='relayBoot']", line).val(_relay.boot);
  928. $("select[name='relayPulse']", line).val(_relay.pulse);
  929. $("input[name='relayTime']", line).val(_relay.pulse_time);
  930. if (schema.includes("sch_last")) {
  931. $("input[name='relayLastSch']", line)
  932. .prop("checked", _relay.sch_last)
  933. .attr("id", "relayLastSch" + id)
  934. .attr("name", "relayLastSch" + id)
  935. .next().attr("for","relayLastSch" + (id));
  936. }
  937. if (schema.includes("group")) {
  938. $("input[name='mqttGroup']", line).val(_relay.group);
  939. }
  940. if (schema.includes("group_sync")) {
  941. $("select[name='mqttGroupSync']", line).val(_relay.group_sync);
  942. }
  943. if (schema.includes("on_disc")) {
  944. $("select[name='relayOnDisc']", line).val(_relay.on_disc);
  945. }
  946. setOriginalsFromValues($("input,select", line));
  947. line.appendTo("#relayConfig");
  948. // Populate the relay SELECTs on the configuration panel
  949. $("select.isrelay").append(
  950. $("<option></option>")
  951. .attr("value", id)
  952. .text(name)
  953. );
  954. ++id;
  955. });
  956. }
  957. // -----------------------------------------------------------------------------
  958. // Sensors & Magnitudes
  959. // -----------------------------------------------------------------------------
  960. //removeIf(!sensor)
  961. function initMagnitudes(data) {
  962. // check if already initialized (each magnitude is inside div.pure-g)
  963. var done = $("#magnitudes > div").length;
  964. if (done > 0) { return; }
  965. var size = data.size;
  966. for (var i=0; i<size; ++i) {
  967. var magnitude = {
  968. "name": MagnitudeNames[data.type[i]] + " #" + parseInt(data.index[i], 10),
  969. "units": data.units[i],
  970. "description": data.description[i]
  971. };
  972. Magnitudes.push(magnitude);
  973. var line = loadConfigTemplate("magnitudeTemplate");
  974. $("label", line).html(magnitude.name);
  975. $("input", line).attr("data", i);
  976. $("div.sns-desc", line).html(magnitude.description);
  977. $("div.sns-info", line).hide();
  978. line.appendTo("#magnitudes");
  979. }
  980. }
  981. //endRemoveIf(!sensor)
  982. // -----------------------------------------------------------------------------
  983. // Curtains
  984. // -----------------------------------------------------------------------------
  985. //removeIf(!curtain)
  986. //Create the controls for one curtain. It is called when curtain is updated (so created the first time)
  987. //Let this there as we plan to have more than one curtain per switch
  988. function initCurtain(data) {
  989. var current = $("#curtains > div").length;
  990. if (current > 0) { return; }
  991. // add and init curtain template, prepare multi switches
  992. var line = loadConfigTemplate("curtainTemplate");
  993. $(line).find(".button-curtain-open").on("click", function() {
  994. sendAction("curtainAction", {button: 1});
  995. $(this).css('background', 'red');
  996. });
  997. $(line).find(".button-curtain-pause").on("click", function() {
  998. sendAction("curtainAction", {button: 0});
  999. $(this).css('background', 'red');
  1000. });
  1001. $(line).find(".button-curtain-close").on("click", function() {
  1002. sendAction("curtainAction", {button: 2});
  1003. $(this).css('background', 'red');
  1004. });
  1005. line.appendTo("#curtains");
  1006. // init curtain slider
  1007. $("#curtainSet").on("change", function() {
  1008. var value = $(this).val();
  1009. var parent = $(this).parents(".pure-g");
  1010. $("span", parent).html(value);
  1011. sendAction("curtainAction", {position: value});
  1012. });
  1013. }
  1014. function initCurtainConfig(data) {
  1015. var current = $("#curtainConfig > legend").length; // there is a legend per relay
  1016. if (current > 0) { return; }
  1017. // Populate the curtain select
  1018. $("select.iscurtain").append(
  1019. $("<option></option>")
  1020. .attr("value", "0")
  1021. .text("Curtain #" + "0")
  1022. );
  1023. }
  1024. //endRemoveIf(!curtain)
  1025. // -----------------------------------------------------------------------------
  1026. // Lights
  1027. // -----------------------------------------------------------------------------
  1028. //removeIf(!light)
  1029. function colorToHsvString(color) {
  1030. var h = String(Math.round(color.hsv.h));
  1031. var s = String(Math.round(color.hsv.s));
  1032. var v = String(Math.round(color.hsv.v));
  1033. return h + "," + s + "," + v;
  1034. }
  1035. function hsvStringToColor(hsv) {
  1036. var parts = hsv.split(",");
  1037. return {
  1038. h: parseInt(parts[0]),
  1039. s: parseInt(parts[1]),
  1040. v: parseInt(parts[2])
  1041. };
  1042. }
  1043. function colorSlider(type) {
  1044. return {component: iro.ui.Slider, options: {sliderType: type}};
  1045. }
  1046. function colorWheel() {
  1047. return {component: iro.ui.Wheel, options: {}};
  1048. }
  1049. function colorBox() {
  1050. return {component: iro.ui.Box, options: {}};
  1051. }
  1052. function updateColor(mode, value) {
  1053. if (colorPicker) {
  1054. if (mode === "rgb") {
  1055. colorPicker.color.hexString = value;
  1056. } else if (mode === "hsv") {
  1057. colorPicker.color.hsv = hsvStringToColor(value);
  1058. }
  1059. return;
  1060. }
  1061. // TODO: useRGB -> ltWheel?
  1062. // TODO: always show wheel + sliders like before?
  1063. var layout = []
  1064. if (mode === "rgb") {
  1065. layout.push(colorWheel());
  1066. layout.push(colorSlider("value"));
  1067. } else if (mode === "hsv") {
  1068. layout.push(colorBox());
  1069. layout.push(colorSlider("hue"));
  1070. }
  1071. var options = {
  1072. color: (mode === "rgb") ? value : hsvStringToColor(value),
  1073. layout: layout
  1074. };
  1075. colorPicker = new iro.ColorPicker("#color", options);
  1076. colorPicker.on("input:change", function(color) {
  1077. if (mode === "rgb") {
  1078. sendAction("color", {rgb: color.hexString});
  1079. } else if (mode === "hsv") {
  1080. sendAction("color", {hsv: colorToHsvString(color)});
  1081. }
  1082. });
  1083. }
  1084. function initCCT() {
  1085. // check if already initialized
  1086. var done = $("#cct > div").length;
  1087. if (done > 0) { return; }
  1088. $("#miredsTemplate").children().clone().appendTo("#cct");
  1089. $("#mireds").on("change", function() {
  1090. var value = $(this).val();
  1091. var parent = $(this).parents(".pure-g");
  1092. $("span", parent).html(value);
  1093. sendAction("mireds", {mireds: value});
  1094. });
  1095. }
  1096. function initChannels(num) {
  1097. // check if already initialized
  1098. var done = $("#channels > div").length > 0;
  1099. if (done) { return; }
  1100. // calculate channels to create
  1101. var max = num;
  1102. if (colorPicker) {
  1103. max = num % 3;
  1104. if ((max > 0) & useWhite) {
  1105. max--;
  1106. if (useCCT) {
  1107. max--;
  1108. }
  1109. }
  1110. }
  1111. var start = num - max;
  1112. var onChannelSliderChange = function() {
  1113. var id = $(this).attr("data");
  1114. var value = $(this).val();
  1115. var parent = $(this).parents(".pure-g");
  1116. $("span", parent).html(value);
  1117. sendAction("channel", {id: id, value: value});
  1118. };
  1119. // add channel templates
  1120. var i = 0;
  1121. for (i=0; i<max; i++) {
  1122. var channel_id = start + i;
  1123. var line = loadTemplate("channelTemplate");
  1124. $("span.slider", line).attr("data", channel_id);
  1125. $("input.slider", line).attr("data", channel_id).on("change", onChannelSliderChange);
  1126. $("label", line).html("Channel #" + channel_id);
  1127. line.appendTo("#channels");
  1128. }
  1129. // Init channel dropdowns
  1130. for (i=0; i<num; i++) {
  1131. $("select.islight").append(
  1132. $("<option></option>").attr("value",i).text("Channel #" + i));
  1133. }
  1134. // add brightness template
  1135. var line = loadTemplate("brightnessTemplate");
  1136. line.appendTo("#channels");
  1137. // init bright slider
  1138. $("#brightness").on("change", function() {
  1139. var value = $(this).val();
  1140. var parent = $(this).parents(".pure-g");
  1141. $("span", parent).html(value);
  1142. sendAction("brightness", {value: value});
  1143. });
  1144. }
  1145. //endRemoveIf(!light)
  1146. // -----------------------------------------------------------------------------
  1147. // RFBridge
  1148. // -----------------------------------------------------------------------------
  1149. //removeIf(!rfbridge)
  1150. function rfbLearn() {
  1151. var parent = $(this).parents(".pure-g");
  1152. var input = $("input", parent);
  1153. sendAction("rfblearn", {id: input.attr("data-id"), status: input.attr("data-status")});
  1154. }
  1155. function rfbForget() {
  1156. var parent = $(this).parents(".pure-g");
  1157. var input = $("input", parent);
  1158. sendAction("rfbforget", {id: input.attr("data-id"), status: input.attr("data-status")});
  1159. }
  1160. function rfbSend() {
  1161. var parent = $(this).parents(".pure-g");
  1162. var input = $("input", parent);
  1163. sendAction("rfbsend", {id: input.attr("data-id"), status: input.attr("data-status"), data: input.val()});
  1164. }
  1165. function addRfbNode() {
  1166. var numNodes = $("#rfbNodes > legend").length;
  1167. var line = loadTemplate("rfbNodeTemplate");
  1168. $("span", line).html(numNodes);
  1169. $(line).find("input").each(function() {
  1170. this.dataset["id"] = numNodes;
  1171. });
  1172. $(line).find(".button-rfb-learn").on("click", rfbLearn);
  1173. $(line).find(".button-rfb-forget").on("click", rfbForget);
  1174. $(line).find(".button-rfb-send").on("click", rfbSend);
  1175. line.appendTo("#rfbNodes");
  1176. return line;
  1177. }
  1178. //endRemoveIf(!rfbridge)
  1179. // -----------------------------------------------------------------------------
  1180. // LightFox
  1181. // -----------------------------------------------------------------------------
  1182. //removeIf(!lightfox)
  1183. function lightfoxLearn() {
  1184. sendAction("lightfoxLearn", {});
  1185. }
  1186. function lightfoxClear() {
  1187. sendAction("lightfoxClear", {});
  1188. }
  1189. function initLightfox(data, relayCount) {
  1190. var numNodes = data.length;
  1191. var i, j;
  1192. for (i=0; i<numNodes; i++) {
  1193. var $line = loadTemplate("lightfoxNodeTemplate");
  1194. $line.find("label > span").text(data[i]["id"]);
  1195. $line.find("select").each(function() {
  1196. $(this).attr("name", "btnRelay" + data[i]["id"]);
  1197. for (j=0; j < relayCount; j++) {
  1198. $(this).append($("<option >").attr("value", j).text("Switch #" + j));
  1199. }
  1200. $(this).val(data[i]["relay"]);
  1201. status = !status;
  1202. });
  1203. setOriginalsFromValues($("input,select", $line));
  1204. $line.appendTo("#lightfoxNodes");
  1205. }
  1206. var $panel = $("#panel-lightfox")
  1207. $(".button-lightfox-learn").off("click").click(lightfoxLearn);
  1208. $(".button-lightfox-clear").off("click").click(lightfoxClear);
  1209. }
  1210. //endRemoveIf(!lightfox)
  1211. // -----------------------------------------------------------------------------
  1212. // Processing
  1213. // -----------------------------------------------------------------------------
  1214. function processData(data) {
  1215. if (debug) console.log(data);
  1216. // title
  1217. if ("app_name" in data) {
  1218. var title = data.app_name;
  1219. if ("app_version" in data) {
  1220. $("span[name=title]").html(data.app_version);
  1221. title = title + " " + data.app_version;
  1222. }
  1223. if ("hostname" in data) {
  1224. title = data.hostname + " - " + title;
  1225. }
  1226. document.title = title;
  1227. }
  1228. Object.keys(data).forEach(function(key) {
  1229. var i;
  1230. var value = data[key];
  1231. // ---------------------------------------------------------------------
  1232. // Web mode
  1233. // ---------------------------------------------------------------------
  1234. if ("webMode" === key) {
  1235. password = (1 === value);
  1236. $("#layout").toggle(!password);
  1237. $("#password").toggle(password);
  1238. }
  1239. // ---------------------------------------------------------------------
  1240. // Actions
  1241. // ---------------------------------------------------------------------
  1242. if ("action" === key) {
  1243. if ("reload" === data.action) {
  1244. doReload(1000);
  1245. }
  1246. return;
  1247. }
  1248. // ---------------------------------------------------------------------
  1249. // RFBridge
  1250. // ---------------------------------------------------------------------
  1251. //removeIf(!rfbridge)
  1252. if ("rfbCount" === key) {
  1253. for (i=0; i<data.rfbCount; i++) { addRfbNode(); }
  1254. return;
  1255. }
  1256. if ("rfb" === key) {
  1257. var rfb = data.rfb;
  1258. var size = rfb.size;
  1259. var start = rfb.start;
  1260. var processOn = ((rfb.on !== undefined) && (rfb.on.length > 0));
  1261. var processOff = ((rfb.off !== undefined) && (rfb.off.length > 0));
  1262. for (let i = 0; i < size; ++i) {
  1263. let selector = template`input[name='rfbcode'][data-id='${id}'][data-status='${status}']`;
  1264. let id = i + start;
  1265. if (processOn) {
  1266. $(selector({"id": id, "status": 1}))
  1267. .val(rfb.on[i]);
  1268. }
  1269. if (processOff) {
  1270. $(selector({"id": id, "status": 0}))
  1271. .val(rfb.off[i]);
  1272. }
  1273. }
  1274. return;
  1275. }
  1276. //endRemoveIf(!rfbridge)
  1277. // ---------------------------------------------------------------------
  1278. // LightFox
  1279. // ---------------------------------------------------------------------
  1280. //removeIf(!lightfox)
  1281. if ("lightfoxButtons" === key) {
  1282. initLightfox(data["lightfoxButtons"], data["lightfoxRelayCount"]);
  1283. return;
  1284. }
  1285. //endRemoveIf(!lightfox)
  1286. // ---------------------------------------------------------------------
  1287. // RFM69
  1288. // ---------------------------------------------------------------------
  1289. //removeIf(!rfm69)
  1290. if (key == "packet") {
  1291. var packet = data.packet;
  1292. var d = new Date();
  1293. packets.row.add([
  1294. d.toLocaleTimeString('en-US', { hour12: false }),
  1295. packet.senderID,
  1296. packet.packetID,
  1297. packet.targetID,
  1298. packet.key,
  1299. packet.value,
  1300. packet.rssi,
  1301. packet.duplicates,
  1302. packet.missing,
  1303. ]).draw(false);
  1304. return;
  1305. }
  1306. if (key == "mapping") {
  1307. for (var i in data.mapping) {
  1308. // add a new row
  1309. addMapping();
  1310. // get group
  1311. var line = $("#mapping .pure-g")[i];
  1312. // fill in the blanks
  1313. var mapping = data.mapping[i];
  1314. Object.keys(mapping).forEach(function(key) {
  1315. var id = "input[name=" + key + "]";
  1316. if ($(id, line).length) $(id, line).val(mapping[key]);
  1317. });
  1318. setOriginalsFromValues($("input", line));
  1319. }
  1320. return;
  1321. }
  1322. //endRemoveIf(!rfm69)
  1323. // ---------------------------------------------------------------------
  1324. // RPN Rules
  1325. // ---------------------------------------------------------------------
  1326. if (key == "rpnRules") {
  1327. for (var i in data.rpnRules) {
  1328. // add a new row
  1329. addRPNRule();
  1330. // get group
  1331. var line = $("#rpnRules .pure-g")[i];
  1332. // fill in the blanks
  1333. var rule = data.rpnRules[i];
  1334. $("input", line).val(rule);
  1335. setOriginalsFromValues($("input", line));
  1336. }
  1337. return;
  1338. }
  1339. if (key == "rpnTopics") {
  1340. for (var i in data.rpnTopics) {
  1341. // add a new row
  1342. addRPNTopic();
  1343. // get group
  1344. var line = $("#rpnTopics .pure-g")[i];
  1345. // fill in the blanks
  1346. var topic = data.rpnTopics[i];
  1347. var name = data.rpnNames[i];
  1348. $("input[name='rpnTopic']", line).val(topic);
  1349. $("input[name='rpnName']", line).val(name);
  1350. setOriginalsFromValues($("input", line));
  1351. }
  1352. return;
  1353. }
  1354. if (key == "rpnNames") return;
  1355. // ---------------------------------------------------------------------
  1356. // Curtains
  1357. // ---------------------------------------------------------------------
  1358. //removeIf(!curtain)
  1359. function applyCurtain(a, b) {
  1360. $("#curtainGetPicture").css('background', 'linear-gradient(' + a + ', black ' + b + '%, #a0d6ff ' + b + '%)');
  1361. }
  1362. if ("curtainState" === key) {
  1363. initCurtain();
  1364. switch(value.type) {
  1365. case '0': //Roller
  1366. default:
  1367. applyCurtain('180deg', value.get);
  1368. break;
  1369. case '1': //One side left to right
  1370. applyCurtain('90deg', value.get);
  1371. break;
  1372. case '2': //One side right to left
  1373. applyCurtain('270deg', value.get);
  1374. break;
  1375. case '3': //Two sides
  1376. $("#curtainGetPicture").css('background', 'linear-gradient(90deg, black ' + value.get/2 + '%, #a0d6ff ' + value.get/2 + '% ' + (100 - value.get/2) + '%, black ' + (100 - value.get/2) + '%)');
  1377. break;
  1378. }
  1379. $("#curtainSet").val(value.set);
  1380. if(!value.moving) {
  1381. $("button.curtain-button").css('background', 'rgb(66, 184, 221)');
  1382. } else {
  1383. if(!value.button)
  1384. $("button.button-curtain-pause").css('background', 'rgb(192, 0, 0)');
  1385. else if(value.button == 1) {
  1386. $("button.button-curtain-close").css('background', 'rgb(66, 184, 221)');
  1387. $("button.button-curtain-open").css('background', 'rgb(192, 0, 0)');
  1388. }
  1389. else if(value.button == 2) {
  1390. $("button.button-curtain-open").css('background', 'rgb(66, 184, 221)');
  1391. $("button.button-curtain-close").css('background', 'rgb(192, 0, 0)');
  1392. }
  1393. }
  1394. return;
  1395. }
  1396. //endRemoveIf(!curtain)
  1397. // ---------------------------------------------------------------------
  1398. // Lights
  1399. // ---------------------------------------------------------------------
  1400. //removeIf(!light)
  1401. if ("lightstate" === key) {
  1402. $("#color").toggle(value);
  1403. return;
  1404. }
  1405. if (("rgb" === key) || ("hsv" === key)) {
  1406. updateColor(key, value);
  1407. return;
  1408. }
  1409. if ("brightness" === key) {
  1410. $("#brightness").val(value);
  1411. $("span.brightness").html(value);
  1412. return;
  1413. }
  1414. if ("channels" === key) {
  1415. var len = value.length;
  1416. initChannels(len);
  1417. for (i in value) {
  1418. var ch = value[i];
  1419. $("input.slider[data=" + i + "]").val(ch);
  1420. $("span.slider[data=" + i + "]").html(ch);
  1421. }
  1422. return;
  1423. }
  1424. if ("mireds" === key) {
  1425. $("#mireds").attr("min", value["cold"]);
  1426. $("#mireds").attr("max", value["warm"]);
  1427. $("#mireds").val(value["value"]);
  1428. $("span.mireds").html(value["value"]);
  1429. return;
  1430. }
  1431. if ("useWhite" === key) {
  1432. useWhite = value;
  1433. }
  1434. if ("useCCT" === key) {
  1435. initCCT();
  1436. useCCT = value;
  1437. }
  1438. //endRemoveIf(!light)
  1439. // ---------------------------------------------------------------------
  1440. // Sensors & Magnitudes
  1441. // ---------------------------------------------------------------------
  1442. //removeIf(!sensor)
  1443. if ("snsErrors" === key) {
  1444. for (var index in value) {
  1445. var type = value[index][0];
  1446. var name = value[index][1];
  1447. MagnitudeErrors[type] = name;
  1448. }
  1449. return;
  1450. }
  1451. if ("snsMagnitudes" === key) {
  1452. for (var index in value) {
  1453. var type = value[index][0];
  1454. var prefix = value[index][1];
  1455. var name = value[index][2];
  1456. MagnitudeNames[type] = name;
  1457. MagnitudeTypePrefixes[type] = prefix;
  1458. MagnitudePrefixTypes[prefix] = type;
  1459. moduleVisible(prefix);
  1460. }
  1461. return;
  1462. }
  1463. if ("magnitudesConfig" === key) {
  1464. initMagnitudes(value);
  1465. return;
  1466. }
  1467. if ("magnitudes" === key) {
  1468. for (var i=0; i<value.size; ++i) {
  1469. var inputElem = $("input[name='magnitude'][data='" + i + "']");
  1470. var infoElem = inputElem.parent().parent().find("div.sns-info");
  1471. var error = value.error[i] || 0;
  1472. var text = (0 === error)
  1473. ? value.value[i] + Magnitudes[i].units
  1474. : MagnitudeErrors[error];
  1475. inputElem.val(text);
  1476. if (value.info !== undefined) {
  1477. var info = value.info[i] || 0;
  1478. infoElem.toggle(info != 0);
  1479. infoElem.text(info);
  1480. }
  1481. }
  1482. return;
  1483. }
  1484. if ("pzemVisible" === key) {
  1485. $("input[name='snsSave']").prop("disabled", true);
  1486. $("input[name='snsSave']")
  1487. .parent().parent().find(".hint")
  1488. .text("PZEM004 module saves the energy data on it's own")
  1489. return;
  1490. }
  1491. //endRemoveIf(!sensor)
  1492. // ---------------------------------------------------------------------
  1493. // WiFi
  1494. // ---------------------------------------------------------------------
  1495. if ("wifi" === key) {
  1496. maxNetworks = parseInt(value["max"], 10);
  1497. value["networks"].forEach(function(network) {
  1498. addNetwork(fromSchema(network, value.schema));
  1499. });
  1500. return;
  1501. }
  1502. if ("scanResult" === key) {
  1503. $("div.scan.loading").hide();
  1504. $("#button-wifi-scan").attr("disabled", false);
  1505. $("#scanResult").show();
  1506. }
  1507. // -----------------------------------------------------------------------------
  1508. // Relays scheduler
  1509. // -----------------------------------------------------------------------------
  1510. if ("schedules" === key) {
  1511. $("#schedules").attr("data-settings-max", value.max);
  1512. for (var i=0; i<value.size; ++i) {
  1513. // XXX: no
  1514. var sch_map = {};
  1515. Object.keys(value).forEach(function(key) {
  1516. if ("size" == key) return;
  1517. if ("max" == key) return;
  1518. sch_map[key] = value[key][i];
  1519. });
  1520. addSchedule(sch_map);
  1521. }
  1522. return;
  1523. }
  1524. // ---------------------------------------------------------------------
  1525. // Relays
  1526. // ---------------------------------------------------------------------
  1527. if ("relayConfig" === key) {
  1528. initRelays(value);
  1529. initRelayConfig(value);
  1530. return;
  1531. }
  1532. if ("relayState" === key) {
  1533. updateRelays(value);
  1534. return;
  1535. }
  1536. // ---------------------------------------------------------------------
  1537. // Curtain(s)
  1538. // ---------------------------------------------------------------------
  1539. //removeIf(!curtain)
  1540. // Relay configuration
  1541. if ("curtainConfig" === key) {
  1542. initCurtainConfig(value);
  1543. return;
  1544. }
  1545. //endRemoveIf(!curtain)
  1546. // ---------------------------------------------------------------------
  1547. // LEDs
  1548. // ---------------------------------------------------------------------
  1549. if ("led" === key) {
  1550. if($("#ledConfig > div").length > 0) return;
  1551. var schema = value["schema"];
  1552. value["list"].forEach(function(led_data, index) {
  1553. var line = loadConfigTemplate("ledConfigTemplate");
  1554. $("span.id", line).html(index);
  1555. $("select", line).attr("data", index);
  1556. $("input", line).attr("data", index);
  1557. var led = fromSchema(led_data, schema);
  1558. $("select[name='ledGPIO']", line).val(led.GPIO);
  1559. // XXX: checkbox implementation depends on unique id
  1560. // $("input[name='ledInv']", line).val(led.Inv);
  1561. $("select[name='ledMode']", line).val(led.Mode);
  1562. $("input[name='ledRelay']", line).val(led.Relay);
  1563. setOriginalsFromValues($("input,select", line));
  1564. line.appendTo("#ledConfig");
  1565. });
  1566. return;
  1567. }
  1568. // ---------------------------------------------------------------------
  1569. // Domoticz
  1570. // ---------------------------------------------------------------------
  1571. // Domoticz - Relays
  1572. if ("dczRelays" === key) {
  1573. createRelayList(value, "dczRelays", "dczRelayTemplate");
  1574. return;
  1575. }
  1576. // Domoticz - Magnitudes
  1577. //removeIf(!sensor)
  1578. if ("dczMagnitudes" === key) {
  1579. createMagnitudeList(value, "dczMagnitudes", "dczMagnitudeTemplate");
  1580. return;
  1581. }
  1582. //endRemoveIf(!sensor)
  1583. // ---------------------------------------------------------------------
  1584. // Thingspeak
  1585. // ---------------------------------------------------------------------
  1586. // Thingspeak - Relays
  1587. if ("tspkRelays" === key) {
  1588. createRelayList(value, "tspkRelays", "tspkRelayTemplate");
  1589. return;
  1590. }
  1591. // Thingspeak - Magnitudes
  1592. //removeIf(!sensor)
  1593. if ("tspkMagnitudes" === key) {
  1594. createMagnitudeList(value, "tspkMagnitudes", "tspkMagnitudeTemplate");
  1595. return;
  1596. }
  1597. //endRemoveIf(!sensor)
  1598. // ---------------------------------------------------------------------
  1599. // HTTP API
  1600. // ---------------------------------------------------------------------
  1601. // Auto generate an APIKey if none defined yet
  1602. if ("apiVisible" === key) {
  1603. if (data.apiKey === undefined || data.apiKey === "") {
  1604. generateAPIKey();
  1605. }
  1606. }
  1607. // ---------------------------------------------------------------------
  1608. // General
  1609. // ---------------------------------------------------------------------
  1610. if ("saved" === key) {
  1611. configurationSaved = value;
  1612. return;
  1613. }
  1614. if ("message" === key) {
  1615. window.alert(value);
  1616. return;
  1617. }
  1618. // Web log
  1619. if ("weblog" === key) {
  1620. send("{}");
  1621. var msg = value["msg"];
  1622. var pre = value["pre"];
  1623. for (var i=0; i < msg.length; ++i) {
  1624. if (pre[i]) {
  1625. $("#weblog").append(new Text(pre[i]));
  1626. }
  1627. $("#weblog").append(new Text(msg[i]));
  1628. }
  1629. followScroll("weblog");
  1630. return;
  1631. }
  1632. // Enable options
  1633. var position = key.indexOf("Visible");
  1634. if (position > 0 && position === key.length - 7) {
  1635. var module = key.slice(0,-7);
  1636. moduleVisible(module);
  1637. return;
  1638. }
  1639. if ("deviceip" === key) {
  1640. var a_href = $("span[name='" + key + "']").parent();
  1641. a_href.attr("href", "//" + value);
  1642. a_href.next().attr("href", "telnet://" + value);
  1643. }
  1644. if ("now" === key) {
  1645. now = parseInt(value, 10);
  1646. return;
  1647. }
  1648. if ("free_size" === key) {
  1649. free_size = parseInt(value, 10);
  1650. }
  1651. // Pre-process
  1652. if ("mqttStatus" === key) {
  1653. value = value ? "CONNECTED" : "NOT CONNECTED";
  1654. }
  1655. if ("ntpStatus" === key) {
  1656. value = value ? "SYNC'D" : "NOT SYNC'D";
  1657. }
  1658. if ("uptime" === key) {
  1659. ago = 0;
  1660. var uptime = parseInt(value, 10);
  1661. var seconds = uptime % 60; uptime = parseInt(uptime / 60, 10);
  1662. var minutes = uptime % 60; uptime = parseInt(uptime / 60, 10);
  1663. var hours = uptime % 24; uptime = parseInt(uptime / 24, 10);
  1664. var days = uptime;
  1665. value = days + "d " + zeroPad(hours, 2) + "h " + zeroPad(minutes, 2) + "m " + zeroPad(seconds, 2) + "s";
  1666. }
  1667. //removeIf(!thermostat)
  1668. if ("tmpUnits" == key) {
  1669. $("span.tmpUnit").html(data[key] == 3 ? "ºF" : "ºC");
  1670. }
  1671. //endRemoveIf(!thermostat)
  1672. // ---------------------------------------------------------------------
  1673. // Matching
  1674. // ---------------------------------------------------------------------
  1675. var elems = [];
  1676. var pre;
  1677. var post;
  1678. // Look for INPUTs
  1679. var input = $("input[name='" + key + "']");
  1680. if (input.length > 0) {
  1681. if (input.attr("type") === "checkbox") {
  1682. input.prop("checked", value);
  1683. } else if (input.attr("type") === "radio") {
  1684. input.val([value]);
  1685. } else {
  1686. pre = input.attr("pre") || "";
  1687. post = input.attr("post") || "";
  1688. input.val(pre + value + post);
  1689. }
  1690. elems.push(input);
  1691. }
  1692. // Look for SPANs
  1693. var span = $("span[name='" + key + "']");
  1694. if (span.length > 0) {
  1695. if (Array.isArray(value)) {
  1696. value.forEach(function(elem) {
  1697. span.append(elem);
  1698. span.append('</br>');
  1699. elems.push(span);
  1700. });
  1701. } else {
  1702. pre = span.attr("pre") || "";
  1703. post = span.attr("post") || "";
  1704. span.html(pre + value + post);
  1705. elems.push(span);
  1706. }
  1707. }
  1708. // Look for SELECTs
  1709. var select = $("select[name='" + key + "']");
  1710. if (select.length > 0) {
  1711. if (select.attr("multiple") !== undefined) {
  1712. select.val(bitsetToValues(value));
  1713. } else {
  1714. select.val(value);
  1715. }
  1716. elems.push(select);
  1717. }
  1718. setOriginalsFromValues($(elems));
  1719. });
  1720. }
  1721. function hasChanged() {
  1722. var newValue, originalValue;
  1723. if ($(this).attr("type") === "checkbox") {
  1724. newValue = $(this).prop("checked");
  1725. originalValue = ($(this).attr("original") === "true");
  1726. } else {
  1727. newValue = $(this).val();
  1728. originalValue = $(this).attr("original");
  1729. }
  1730. if ($(this).attr("multiple") !== undefined) {
  1731. newValue = newValue.join(",");
  1732. }
  1733. var hasChanged = ("true" === $(this).attr("hasChanged"));
  1734. var action = $(this).attr("action");
  1735. if (typeof originalValue === "undefined") { return; }
  1736. if ("none" === action) { return; }
  1737. if (newValue !== originalValue) {
  1738. if (!hasChanged) {
  1739. ++numChanged;
  1740. if ("reconnect" === action) { ++numReconnect; }
  1741. if ("reboot" === action) { ++numReboot; }
  1742. if ("reload" === action) { ++numReload; }
  1743. }
  1744. $(this).attr("hasChanged", true);
  1745. } else {
  1746. if (hasChanged) {
  1747. --numChanged;
  1748. if ("reconnect" === action) { --numReconnect; }
  1749. if ("reboot" === action) { --numReboot; }
  1750. if ("reload" === action) { --numReload; }
  1751. }
  1752. $(this).attr("hasChanged", false);
  1753. }
  1754. }
  1755. // -----------------------------------------------------------------------------
  1756. // Init & connect
  1757. // -----------------------------------------------------------------------------
  1758. function initUrls(root) {
  1759. var paths = ["ws", "upgrade", "config", "auth"];
  1760. urls["root"] = root;
  1761. paths.forEach(function(path) {
  1762. urls[path] = new URL(path, root);
  1763. urls[path].protocol = root.protocol;
  1764. });
  1765. if (root.protocol == "https:") {
  1766. urls.ws.protocol = "wss:";
  1767. } else {
  1768. urls.ws.protocol = "ws:";
  1769. }
  1770. }
  1771. function connectToURL(url) {
  1772. initUrls(url);
  1773. fetch(urls.auth.href, {
  1774. 'method': 'GET',
  1775. 'cors': true,
  1776. 'credentials': 'same-origin'
  1777. }).then(function(response) {
  1778. // Nothing to do, reload page and retry
  1779. if (response.status != 200) {
  1780. doReload(5000);
  1781. return;
  1782. }
  1783. // update websock object
  1784. if (websock) { websock.close(); }
  1785. websock = new WebSocket(urls.ws.href);
  1786. websock.onmessage = function(evt) {
  1787. var data = {};
  1788. try {
  1789. data = JSON.parse(evt.data
  1790. .replace(/\n/g, "\\n")
  1791. .replace(/\r/g, "\\r")
  1792. .replace(/\t/g, "\\t"));
  1793. } catch (e) {
  1794. console.log(e);
  1795. }
  1796. processData(data);
  1797. };
  1798. websock.onclose = function(evt) {
  1799. clearInterval(ws_pingpong);
  1800. if (window.confirm("Connection lost with the device, click OK to refresh the page")) {
  1801. $("#layout").toggle(false);
  1802. window.location.reload();
  1803. }
  1804. }
  1805. websock.onopen = function(evt) {
  1806. ws_pingpong = setInterval(function() { sendAction("ping", {}); }, 5000);
  1807. }
  1808. }).catch(function(error) {
  1809. console.log(error);
  1810. doReload(5000);
  1811. });
  1812. }
  1813. function connect(host) {
  1814. if (!host.startsWith("http:") && !host.startsWith("https:")) {
  1815. host = "http://" + host;
  1816. }
  1817. connectToURL(new URL(host));
  1818. }
  1819. function connectToCurrentURL() {
  1820. connectToURL(new URL(window.location));
  1821. }
  1822. $(function() {
  1823. // most of the time, we want this unconditionally for all <a href="..."></a>
  1824. $("a.external")
  1825. .attr("target", "_blank")
  1826. .attr("rel", "noopener")
  1827. .attr("tabindex", "-1");
  1828. createCheckboxes();
  1829. setInterval(function() { keepTime(); }, 1000);
  1830. $(".password-reveal").on("click", toggleVisiblePassword);
  1831. $("#menuLink").on("click", toggleMenu);
  1832. $(".pure-menu-link").on("click", showPanel);
  1833. $("progress").attr({ value: 0, max: 100 });
  1834. $("#button-wifi-scan").on("click", doScan);
  1835. $(".button-update").on("click", doUpdate);
  1836. $(".button-update-password").on("click", doUpdatePassword);
  1837. $(".button-generate-password").on("click", doGeneratePassword);
  1838. $(".button-reboot").on("click", doReboot);
  1839. $(".button-reconnect").on("click", doReconnect);
  1840. $(".button-dbgcmd").on("click", doDebugCommand);
  1841. $("input[name='dbgcmd']").enterKey(doDebugCommand);
  1842. $(".button-dbg-clear").on("click", doDebugClear);
  1843. $(".button-settings-backup").on("click", doBackup);
  1844. $(".button-settings-restore").on("click", doRestore);
  1845. $(".button-settings-factory").on("click", doFactoryReset);
  1846. $("#uploader").on("change", onFileUpload);
  1847. $(".button-upgrade").on("click", doUpgrade);
  1848. //removeIf(!garland)
  1849. $(".checkbox-garland-enable").on("change", function() {
  1850. sendAction("garland_switch", {status: $(this).prop("checked") ? 1 : 0});
  1851. });
  1852. $(".slider-garland-brightness").on("change", function() {
  1853. sendAction("garland_set_brightness", {brightness: $(this)[0].value});
  1854. });
  1855. $(".slider-garland-speed").on("change", function() {
  1856. sendAction("garland_set_speed", {speed: $(this)[0].value});
  1857. });
  1858. $(".button-garland-set-default").on("click", function() {
  1859. sendAction("garland_set_default", {});
  1860. });
  1861. //endRemoveIf(!garland)
  1862. //removeIf(!thermostat)
  1863. $(".button-thermostat-reset-counters").on('click', doResetThermostatCounters);
  1864. //endRemoveIf(!thermostat)
  1865. $(".button-apikey").on("click", generateAPIKey);
  1866. $(".button-upgrade-browse").on("click", function() {
  1867. $("input[name='upgrade']")[0].click();
  1868. return false;
  1869. });
  1870. $("input[name='upgrade']").change(function (){
  1871. var file = this.files[0];
  1872. $("input[name='filename']").val(file.name);
  1873. });
  1874. $(".button-add-network").on("click", function() {
  1875. $(".more", addNetwork()).toggle();
  1876. });
  1877. $(".button-add-switch-schedule").on("click", function() {
  1878. addSchedule({schType: 1, schSwitch: -1});
  1879. });
  1880. //removeIf(!light)
  1881. $(".button-add-light-schedule").on("click", function() {
  1882. addSchedule({schType: 2, schSwitch: -1});
  1883. });
  1884. //endRemoveIf(!light)
  1885. //removeIf(!curtain)
  1886. $(".button-add-curtain-schedule").on("click", function() {
  1887. addSchedule({schType: 3, schSwitch: -1});
  1888. });
  1889. //endRemoveIf(!curtain)
  1890. $(".button-add-rpnrule").on('click', addRPNRule);
  1891. $(".button-add-rpntopic").on('click', addRPNTopic);
  1892. $(".button-del-parent").on('click', delParent);
  1893. //removeIf(!rfm69)
  1894. $(".button-add-mapping").on('click', addMapping);
  1895. $(".button-clear-counts").on('click', doClearCounts);
  1896. $(".button-clear-messages").on('click', doClearMessages);
  1897. $(".button-clear-filters").on('click', doClearFilters);
  1898. $('#packets tbody').on('mousedown', 'td', doFilter);
  1899. packets = $('#packets').DataTable({
  1900. "paging": false
  1901. });
  1902. for (var i = 0; i < packets.columns()[0].length; i++) {
  1903. filters[i] = false;
  1904. }
  1905. //endRemoveIf(!rfm69)
  1906. $(".gpio-select").each(function(_, elem) {
  1907. initSelectGPIO(elem)
  1908. });
  1909. $(document).on("change", "input", hasChanged);
  1910. $(document).on("change", "select", hasChanged);
  1911. $("textarea").on("dblclick", function() { this.select(); });
  1912. resetOriginals();
  1913. $(".group-settings").each(function() {
  1914. groupSettingsObserver.observe(this, {childList: true});
  1915. });
  1916. // don't autoconnect when opening from filesystem
  1917. if (window.location.protocol === "file:") {
  1918. processData({"webMode": 0});
  1919. return;
  1920. }
  1921. // Check host param in query string
  1922. var search = new URLSearchParams(window.location.search),
  1923. host = search.get("host");
  1924. if (host !== null) {
  1925. connect(host);
  1926. } else {
  1927. connectToCurrentURL();
  1928. }
  1929. });