
Diesel
Members-
Posts
51 -
Joined
-
Last visited
Everything posted by Diesel
-
After another espresso or 2....some more thoughts to ensure it doesn't impede anyone using it as a bridge to programs, and actually enhances program functionality in Phase A; Phase A (enhancement) Minimal, philosophy-pure changes that help everyone and don’t alter scope. 1)nodedefs.xml — add sends on the existing Virtual Dimmer + include fast on/off (If Virtual’s “dimmer/generic” already exists, extend that nodeDef; otherwise add a “VLDIM” nodeDef.) <nodeDef id="VLDIM" nls="VLDIM" name="Virtual Dimmer" icon="LightDimmer"> <sts> <st id="ST" editor="percent" uom="51" /> </sts> <!-- What this node can ACCEPT from scenes/programs --> <accepts> <cmd id="DON"><p id="value" editor="percent" /></cmd> <cmd id="DOF"/> <cmd id="BRT"/> <cmd id="DIM"/> <cmd id="DFON"/> <cmd id="DFOF"/> </accepts> <!-- What this node can SEND (for Programs to “Control is … then …”) --> <sends> <cmd id="DON"/> <cmd id="DOF"/> <cmd id="BRT"/> <cmd id="DIM"/> <cmd id="DFON"/> <cmd id="DFOF"/> </sends> <!-- Optional program-only convenience --> <cmds> <cmd id="SETLVL"><p id="value" editor="percent" /></cmd> <cmd id="QUERY"/> </cmds> </nodeDef> If your schema uses cmds only and no explicit <accepts>/<sends>, we still add handlers for DFON/DFOF and ensure controller events fire so Programs can “Control … is Fast On/Off”. 2) Node class — just add DFON/DFOF handlers and emit sends In the existing Virtual Dimmer node: def cmd_DON(self, command): # update ST and fire “DON sent” (so Programs can trigger) self._set_local_level(self._value_from(command, default=100)) self.reportCmd('DON') def cmd_DOF(self, command): self._set_local_level(0) self.reportCmd('DOF') def cmd_BRT(self, command): self._step(+self.step_pct) self.reportCmd('BRT') def cmd_DIM(self, command): self._step(-self.step_pct) self.reportCmd('DIM') # NEW: Fast On/Off (for parity with physical dimmer) def cmd_DFON(self, command): self._set_local_level(100) self.reportCmd('DFON') def cmd_DFOF(self, command): self._set_local_level(0) self.reportCmd('DFOF') # SETLVL remains for programmatic level changes (program use) def cmd_SETLVL(self, command): pct = self._value_from(command, default=100) self._set_local_level(pct) # optional: do not report a scene “send”; this is a program-level op reportCmd('XXX') should be whatever the Virtual server currently uses to raise the “Control is …” events so Programs can key off Tap On / Tap Off / Fast On / Fast Off / Brighten / Dim. Result: Users can now write Programs like: If Control ‘Virtual Dimmer’ is Fast On → Then Set Scene ‘SceneXYZ’ On If Control ‘Virtual Dimmer’ is Brighten → Then Network Resource “SceneBRT” …without the plugin doing any scene logic itself. Phase B (Optional - opt in): one-line “Relay” to a target node (still a bridge) Add a single param to the Virtual Dimmer (e.g., FORWARD_TO_NODE). When set, the node mirrors the exact sent command to that node via IoX REST. This doesn't introduce scene logic; it’s just pass-through. Add two drivers/params for visibility & control (optional): GV0 (string) — the target node address (scene group or device), display only. GV1 (bool) — “momentary” cosmetic reset (return ST to 0 after 0.4s), optional. In each send path (DON/DOF/BRT/DIM/DFON/DFOF): if self.forward_to: # For scenes: DON with an optional level argument is supported by IoX REST. # For devices: DON/DOF/BRT/DIM/DFON/DFOF are passed as-is. self._isy.cmd(self.forward_to, 'DON', level_0_to_255) # only when DON has a % value # or self._isy.cmd(self.forward_to, 'DFON') # etc. This still keeps Virtual squarely in “bridge/relay” land: Program patterns (what users can do with Phase A) Single-scene control (ramp by scene presets): - IF Control ‘Virtual Dimmer’ is switched On → Then Set Scene ‘X’ On - IF Control ‘Virtual Dimmer’ is switched Off → Then Set Scene ‘X’ Off Fast on/off: - IF Control ‘Virtual Dimmer’ is switched Fast On → Then Set Scene ‘X’ On (or 100% via NR) - IF Control ‘Virtual Dimmer’ is switched Fast Off → Then Set Scene ‘X’ Off Brighten/Dim stepping: - IF Control ‘Virtual Dimmer’ is Brighten → Then Network Resource calling /rest/nodes/<scene>/cmd/BRT - IF Control ‘Virtual Dimmer’ is Dim → **Then … /DIM` Set exact level (%): - Use SETLVL to set ST (program semantics), then NR: /rest/nodes/<scene>/cmd/DON/<0–255>. - IoX Programs typically don’t pass a % to a Scene “Set On” natively; NR handles it cleanly.
-
Hi @sjenkins I really appreciate you for all the effort you've put into enhancing Virtual, and even more-so for being open to discussion. The issue I'm trying to solve is Insteon's inherent disfunction with scenes. Native Insteon scenes were created by Insteon because they're the only reliable way to simultaneously control multiple devices. Unfortunately, IoX considers scenes to be stateless, but not really. If a device is a member of multiple scenes, all of those scenes are considered to be in the same state as that device, even if that scene was never called. A scene doesn't have a "truth indicator" that indicates whether a scene is on or off. So we end up having to create a spaghetti of programs to work around this disfunction. As we move forward with Insteon to work with HomeKit, Matter, and other platforms, this disfunction becomes an even bigger issue. A multi-way circuit is just an example of this disfunction. Insteon's native approach is to add the devices all into a scene to keep them in sync. It works as expected when controlling any of the physical devices, but not within UDM or any other platform unless you control the scene itself. What's missing? A Virtual device to act like the physical device(s) in that scene. In my mind, a virtual device would behave the same as a keypad button that acts as a scene controller. A keypad button always reflects the true state of it's scene. It turns the scene on when turned on, turns it off when turned off, brightens and dims the scene when pressed. That said, I appreciate your thoughtful response and I understand your “bridge, not replace Programs” philosophy. Giving some more thought on how to keep Virtual within that scope, but still achieve what I believe can be a game changer for everyone, I propose; Phase A (bridge-only): Extend the Virtual Dimmer’s accepts/sends set to include DFON/DFOF alongside the existing DON/DOF/BRT/DIM. The node will handle/emit those events so Programs can key off Fast On/Off in addition to Tap/Dim/Brighten. No scene awareness or coordination in the plugin. Phase B (optional, default off): Add a single optional text param FORWARD_TO_NODE. When set, the dimmer simply relays whatever it sends (DON/DOF/BRT/DIM/DFON/DFOF) to that node via IoX REST. It remains a bridge/relay — still no scene membership knowledge or multi-dimmer logic inside the plugin. Users who prefer pure Programs can leave it blank and get the exact behavior you described. This way Virtual stays true to its mission (bridge signals to Programs), while enabling a parity command set with physical dimmers and, optionally, a tiny relay convenience for those who want it. Let me know your thoughts and whether you need me to elaborate further.
-
# nodes/virtual_dimmer.py import time import requests import udi_interface LOGGER = udi_interface.LOGGER class IsyRest: """Tiny helper to call IoX REST for scene commands.""" def __init__(self, base_url, user, password, verify_ssl=True, timeout=5): self.base = (base_url or '').rstrip('/') self.auth = (user, password) if user else None self.verify = verify_ssl self.timeout = timeout def cmd(self, node_or_scene_addr, command, *args): if not self.base: raise RuntimeError("ISY_BASE_URL not configured") url = f"{self.base}/rest/nodes/{node_or_scene_addr}/cmd/{command}" if args: url += "/" + "/".join(str(a) for a in args) r = requests.get(url, auth=self.auth, verify=self.verify, timeout=self.timeout) r.raise_for_status() return True class VirtualDimmer(udi_interface.Node): """ Virtual Dimmer that: - As a Responder: updates its own ST on DON/DOF/BRT/DIM/etc. - As a Controller: forwards DON/DOF/DFON/DFOF/BRT/DIM/BMAN/SMAN to a native IoX scene (group). """ id = 'VLDIM' # must match nodedefs.xml # Drivers: # ST : 0-100 % level (uom 51) # GV0 : scene/group address this node controls (display) # GV1 : momentary flag 0/1 (if 1, returns to 0% after send) drivers = [ {'driver': 'ST', 'value': 0, 'uom': 51}, {'driver': 'GV0', 'value': 0, 'uom': 56}, {'driver': 'GV1', 'value': 0, 'uom': 2}, ] def __init__(self, polyglot, primary, address, name, isy_rest: IsyRest, scene_addr: str = None, momentary: bool = False, step_pct: int = 3): super().__init__(polyglot, primary, address, name) self.isy = isy_rest self.scene_addr = scene_addr # e.g. 0001, 0012 (group id) self.momentary = 1 if momentary else 0 self.step_pct = max(1, min(20, int(step_pct))) # ---------- lifecycle ---------- def start(self): self.setDriver('GV1', self.momentary, report=True, force=True) if self.scene_addr: try: self.setDriver('GV0', int(self.scene_addr), report=True, force=True) except Exception: # not numeric; ignore—GV0 is display only pass self.reportDrivers() def query(self, command=None): self.reportDrivers() # ---------- helpers ---------- @staticmethod def pct_to_255(pct: int) -> int: pct = max(0, min(100, int(pct))) return round(pct * 255 / 100) def _set_local_level(self, pct: int): pct = max(0, min(100, int(pct))) self.setDriver('ST', pct, report=True, force=True) def _send_scene(self, cmd, *args): if not self.scene_addr: LOGGER.debug(f"{self.address}: no scene_addr configured; skip send {cmd} {args}") return try: self.isy.cmd(self.scene_addr, cmd, *args) except Exception as e: LOGGER.error(f"{self.address}: scene command failed: {cmd} {args} -> {e}") def _momentary_reset(self): if self.momentary: time.sleep(0.4) self._set_local_level(0) # ---------- command handlers ---------- # DON (optionally with level 0-100 from Admin Console) def cmd_DON(self, command): lvl = 100 if command and 'value' in command: try: lvl = int(command.get('value')) except Exception: lvl = 100 self._set_local_level(lvl) # responder behavior self._send_scene('DON', self.pct_to_255(lvl)) # controller behavior self._momentary_reset() def cmd_DOF(self, command): self._set_local_level(0) self._send_scene('DOF') self._momentary_reset() def cmd_DFON(self, command): self._set_local_level(100) self._send_scene('DFON') self._momentary_reset() def cmd_DFOF(self, command): self._set_local_level(0) self._send_scene('DFOF') self._momentary_reset() def cmd_BRT(self, command): new_lvl = min(100, int(self.getDriver('ST')) + self.step_pct) self._set_local_level(new_lvl) self._send_scene('BRT') def cmd_DIM(self, command): new_lvl = max(0, int(self.getDriver('ST')) - self.step_pct) self._set_local_level(new_lvl) self._send_scene('DIM') def cmd_BMAN(self, command): self._send_scene('BMAN') def cmd_SMAN(self, command): self._send_scene('SMAN') # Explicit “Set Level” from programs (0–100) def cmd_SETLVL(self, command): try: pct = int(command.get('value')) except Exception: pct = 100 self._set_local_level(pct) self._send_scene('DON', self.pct_to_255(pct)) self._momentary_reset() commands = { 'DON': cmd_DON, 'DOF': cmd_DOF, 'DFON': cmd_DFON, 'DFOF': cmd_DFOF, 'BRT': cmd_BRT, 'DIM': cmd_DIM, 'BMAN': cmd_BMAN, 'SMAN': cmd_SMAN, 'SETLVL': cmd_SETLVL, 'QUERY': query, }
-
Thanks for this! Ideally, a Virtual Dimmer would behave the same as a physical dimmer for consistency. Consider a potential common use case of a virtual controller of a multi-way circuit scene.Virtual dimmer controls and keeps the scene, and physical dimmers all in sync. Maybe something along these lines? User action (virtual dimmer) Exact behavior to emulate Commands to send (to scene) Notes Single tap ON(top paddle) Go to scene’s programmed On Levels with scene’s ramp DON (no level parameter) Mirrors what a physical dimmer sends when used as a scene controller. Single tap OFF(bottom paddle) Ramp responders to off per scene link DOF Double-tap ON Fast On (immediate to full) DFON Bypasses ramp to 100%. Double-tap OFF Fast Off (immediate off) DFOF Bypasses ramp. Press & hold ON (top hold) Start brightening until release BMAN (once), then periodic BRT until release, then SMAN Repeat rate ~300–400 ms? Press & hold OFF (bottom hold) Start dimming until release BMAN (once), then periodic DIM until release, then SMAN Same repeat rate. Set Level(slider/command with %) Jump scene to specific runtime level DON/<0–255> Useful for admin UI or programmatic ontrol.
-
IMHO option 2 makes the most sense for a virtual dimmer, otherwise with option 1 it behaves like a switch. A virtual dimmer should function in the same way as a physical dimmer or a keypad button added as a scene controller. Holding a Dimmer or Keypad button down brightens or dims a scene. Pressing a keypad button turns a scene ON or OFF. One of the most useful applications for a virtual dimmer as a scene controller is for multi-way circuits. Consider a common 3-way lighting circuit where 2 physical dimmer switches are added to a "3way" scene, both as controllers. BUT in IoX if you control any of the 2 dimmers directly, the other isn't in sync and neither is the scene. The virtual dimmer solves this dysfunctional behaviour where everything remains in sync as it should be.
-
-
@sjenkins Is a virtual dimmer not able to used as a scene controller?
-
Thanks so much! Loaded beta 3.1.14 into another slot. Created a virtual switch and was able to add it as a scene controller - Awesome! {"id": "10", "type": "switch", "name": "My vs Name"} Device type in AC shows as Virtual Switch Created a virtual dimmer but it only allows the option to add it as a scene responder. {"id": "30", "type": "dimmer", "name": "My vd Name"} Device type in AC shows as Virtual Generic The scene I tried adding it to is a 3-way circuit with 2 dimmers.
-
@sjenkins There's some really great integrations in the store, but Virtual is one of the most important. Virtual switches/dimmers as scene controller & responder solves so many issues. Thanks so much for taking the time to make this happen! 😊
-
from polyinterface import Node, LOGGER class VirtualSwitch(Node): """ Virtual Switch Node for PG3x that can act as a controller for a native Insteon scene. """ id = 'virtual_switch' def __init__(self, polyglot, primary, address, name, scene_id=None): super().__init__(polyglot, primary, address, name) self.scene_id = scene_id # Optional: Native Insteon scene this switch controls self.add_driver('ST', 0) # State driver: 0=off, 1=on def start(self): LOGGER.info(f"Starting Virtual Switch '{self.name}' with scene_id={self.scene_id}") # Register command handlers self.poly.on('ON', self.on_command) self.poly.on('OFF', self.off_command) def on_command(self, command=None): """Handle ON command from PG3x or scene activation""" if self.scene_id: self.send_scene_command('ON') self.set_driver('ST', 1) # Update switch state def off_command(self, command=None): """Handle OFF command""" if self.scene_id: self.send_scene_command('OFF') self.set_driver('ST', 0) def send_scene_command(self, cmd): """ Sends the ON/OFF command to the linked Insteon scene via IoX. Replace the below with actual PG3x API call to trigger a scene. """ try: LOGGER.info(f"Sending command '{cmd}' to scene {self.scene_id}") # Example pseudo-call; replace with actual API method # self.poly.iot.send_scene_command(self.scene_id, cmd) except Exception as e: LOGGER.error(f"Failed to send command to scene {self.scene_id}: {e}") def set_scene_id(self, scene_id): """Optional helper to dynamically update the controlled scene""" self.scene_id = scene_id LOGGER.info(f"Virtual Switch '{self.name}' now controls scene {scene_id}")
-
Hi, @sjenkins thank you so much for the work that's gone into this plugin. Any possibility to enhance it so that virtual switches can be a scene controller rather than only a responder? Taking it a step further, also be able to create a virtual dimmer? It would solve a very fundamental and fatal issue with the way IoX considers a scene state.
-
Thanks for taking the time to share your detailed experience with the update on Polisy. I was just about to press that upgrade button (in AC) on my Polisy when I read your post. I have no time to resurrect the dead this week so I'm going to wait for Michel to work out the upgrade bugs before I hit that upgrade button.
-
Hi Lee, Thanks for your help. I've created the variable and related program, and updated the email notif program as you outlined. When trying to rename the variable from Int_1 to something meaningful, it always seems to reset back to Int_1? I'm surprised that there's no simpler (or inherent) way to throttle or limit the number/interval of emails sent when motion is detected? For example: You have a motion sensor in the back yard and want to receive an email notification when there's movement, so you can have a look on your cameras to see what's going on. If the gardner shows up, your inbox gets hammered every second while cuts the grass!
-
Hi Michel, I just want to receive an email notification as soon as the motion sensor is triggered, but only once and then not again for X minutes, otherwise the notifications flood my inbox. I don't want the system to wait 15mins after sensing motion to notify me. For example, the sensor will detect motion when the Gardner is cutting the grass, but currently I would receive 35 emails a minute. Just once would be fine, and then if motion continues 15 or 30mins later, it can send another notification again. How do I achieve this? Thanks for your help.
-
Hi all, Need some help with a new 2420M ver2.0 Motion Sensor and email notification. 2420M is installed outside for night time perimeter security. All seems to work triggering the lights I've specified in the program. Next objective was to send myself an email notification when it sensed motion. So I added the program below and it works, but how do I get it to send just 1 email instead of 15-20 of them? If Control 'Outside / Outside:Motion-Sensor' is switched On Then Send Notification to 'Me' content 'Outside Rear Motion Detected' Wait 15 minutes Else - No Actions - (To add one, press 'Action') It seems like it sends an email each time the sensor detects motion, even a few seconds apart which hammers my inbox. I had hoped adding the 'Wait 15 mins' would help, but no luck. What am I missing?
-
Thanks for the ideas guys. The date range was particularly helpful.
-
Hi all, I think understand the basic concept of a Vacation/Away Mode, but could use some help establishing the best way to use it. Objective is for certain additional security lights to come on at sunset when we're away and turn off early am as well as to turn off some other lights like bedroom lamps that we normally turn off ourselves when we go to bed when we're home. Could you guys give me some examples of how you use the Vacation/Away folder and if its possible to manually "activate" this mode/programs in that folder to not have to rely only on a kpd button status. I'm concerned that if we're away for some days and the power should go out, I presume this kpd button will just reset itself to off. Maybe I'm wrong tho. Thanks for the help.
-
That worked ! Thanks Mike Although I needed to add a wait in there too, otherwise the kpl light turned off even if the scene it controlled was active. I guess it needed more time to evaluate if each of the devices met that condition. Now the program looks like this; If Status 'WatchTV (E)' is not Off And Status 'FamilyRm-LeftLamp' is not On Or Status 'FamilyRm-TvConsole' is not On Or Status 'FamilyRm-RightLamp' is not On Then Wait 1 second Set Scene 'WatchTVStatus' Off Else - No Actions -
-
Ok so I did the following; 1. Created a scene called WatchTVStatus and added only KPL (E) as a responder. 2. Created a program called WatchTVStatus If Status 'WatchTV (E)' is not Off And Status 'FamilyRm-LeftLamp' is not On And Status 'FamilyRm-TvConsole' is not On And Status 'FamilyRm-RightLamp' is not On Then Set Scene 'WatchTVStatus' Off Else - No Actions - Since On= 100%, when I press WatchMovie(F) which reduces all of those lights to 40%, the above program should trigger Status of (E) to change to Off. For some reason it doesn't work
-
Ahh wait I think that's what I was missing. Do you mean to say I need to create a scene with only that specific KPL (E) button in it ? For example create a scene called WatchTVStatus and add KPL button (E) as a responder to it ?
-
Hi Mike, thanks. I've read that thread 3 times and I still can't wrap my head around it. At least not yet. When I created a program to check status on the devices in WatchTV scene so if they were at 40% & 50% (depending on the device) it would mean that WatchMovie was true and should turn off WatchTV. But all that did was turn off all the lights in the scene instead.Doh ! By the way, in the notes section of each device (right click) there's a checkbox for Is Load. What does this checkbox do ?
-
Still very new to the ISY99ir/Pro (thanks Michel it's great!) and need your help understanding how to get some things accomplished. I've spent the week-end installing about 30 SWL & 5 KPL's. In our family room we have various table lamps, spots and lights in our entertainment center. So I've setup 2 specific scenes all setup thru the ISY. Scene: WatchTV Table lamps are on 100% TV console light is on 100% Overhead spots are off Scene: Watch Movie Table lamps at 40% TV console light 50% Overhead spots off. Both scenes work exactly as expected. Now to program them onto the 8 button KPL in the same room. Button E is added as a controller to WatchTV. Button F is added as a controller to WatchMovie Problem: If WatchTV (E) is pressed on and later we want to watch a movie when pressing WatchMovie (F), button (E)WatchTV remains lit. The same problem in reverse going from WatchMovie to WatchTV scenes. I tried adding button (E) to the WatchMovie (F) scene, but that didn't work as expected. What am I missing. I'm sure this is really simple stuff for some of your experts, but I'm stumped. Thanks