roblans / ZWave4Net

ZWave4Net is a .NET library that interfaces with the Aeotec / Aeon Labs Z-Stick.
MIT License
41 stars 34 forks source link

Fixed fibaro multiswitch reports #11

Closed rpbrongers closed 7 years ago

rpbrongers commented 7 years ago

Essentially did the 'ToDo' in there :-) This requires my previous pull request to be implemented too (otherwise the endpointID is not properly reported) Verified with my Fibaro device...works like a charm!

roblans commented 7 years ago

Merged and committed. Thanks for your contribution.

BlueBasher commented 6 years ago

I'm also trying to get the SwicthedOn/Off events to work for a MultiSwitch but just can't get any of the events to trigger. Also have a WallPlug connected to the same application and this events are getting triggered. Can you explain what you did to get it working like a charm?

I've paired the MultiSwitch (Fibaro Double Switch 2) with the Z-Stick. Ran the initialization which is shown in the sample net application, this basically added Group1,2 and 3 associations to Node 1. When I trigger the wall switch the light is switched on but there's no event triggered.

rpbrongers commented 6 years ago

If I remember correctly, I had to make some changes to the multiswitch (multichannel actually) code to make it work with the events, there was something off with the endpoints. I fixed it for my version (happy to share), but I'm afraid I re-structured a lot of things to fit my personal project, so I can't really do a pull request.

I'll find some time later to see if I can summarize what I did.

Meanwhile, can you confirm that bytes come in when you switch on the debug logs? Alternatively, you could verify with the zwavecontroller.exe test tool (this tool used to be on AeonLabs site, but I can't find it anymore). Let me know if you don't have it.


From: Marco van Kimmenade notifications@github.com Sent: Thursday, July 20, 2017 3:32 PM To: roblans/ZWave4Net Cc: rpbrongers; Author Subject: Re: [roblans/ZWave4Net] Fixed fibaro multiswitch reports (#11)

I'm also trying to get the SwicthedOn/Off events to work for a MultiSwitch but just can't get any of the events to trigger. Also have a WallPlug connected to the same application and this events are getting triggered. Can you explain what you did to get it working like a charm?

I've paired the MultiSwitch (Fibaro Double Switch 2) with the Z-Stick. Ran the initialization which is shown in the sample net application, this basically added Group1,2 and 3 associations to Node 1. When I trigger the wall switch the light is switched on but there's no event triggered.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/roblans/ZWave4Net/pull/11#issuecomment-316741081, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AOia9gohye9rwLx7o0aYTWlWhqyhFk4Uks5sP3MNgaJpZM4N172j.

rpbrongers commented 6 years ago

Mmh, those endpoint changes are the ones I submitted (and were merged in the main branch). Other than that, I expanded the code, creating a base class for both my Fibaro multiswitch and my Greenwave 6-switch. But that didn't modify any byte-transformation logic. Still willing to help out, and my earlier question has now become a requirement: "Can you confirm that bytes come in on the Channel when you press the physical switch? Uncommenting the channel.Log = Console.Out in the sample code will help with this. Can you post a log?

BlueBasher commented 6 years ago

Thanks for replying! I'm currently not at home, will enable the debug log tomorrow morning first thing.

BlueBasher commented 6 years ago

The debug log did help, when I made sure associations for Group 1,2 and 3 are linked to the Controller node, I am getting the SwitchBinary and SwitchMultiLevel changed events. But only for 1 of the 2 switches, only the switch you use for inclusion is reporting anything. When pressing the other switch, no data is transmitted at all. And for the switch that is transmitting data, the MultiChannel event, which is also used in the Fibaro MultiSwitch class, is not getting fired.

I've read something about multi channel associations, could it be you also have made some changes in adding associations for this? It looks like I'm having similar issues with a Fibaro MultiSensor, the MotionDetected event is never triggered, but LightMeasured is. That got me thinking about the multi channel associations.

I've attached the logfile my TestApp generated, maybe you see something strange in the data? And for reference below is the code for the TestApp.

ZWave.txt

    public partial class MainWindow : Window
    {
        private ZWaveController controller;
        private Node multiSwitch;

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void ConnectButton_Click(object sender, RoutedEventArgs e)
        {
            var portName = System.IO.Ports.SerialPort.GetPortNames().Where(element => element != "COM1").First();

            controller = new ZWaveController(portName);
            controller.Channel.Log = new TmpTextWriter(LogMessage);

            controller.Open();
            try
            {
                var nodes = await controller.GetNodes();
                multiSwitch = nodes[3];

                var protocolInfo = await multiSwitch.GetProtocolInfo();
                LogMessage($"Node: {multiSwitch}, Generic = {protocolInfo.GenericType}, Basic = {protocolInfo.BasicType}, Listening = {protocolInfo.IsListening} ");

                var supportedCommandClasses = await multiSwitch.GetSupportedCommandClasses();
                LogMessage($"Supported commandclasses:\n{string.Join("\n", supportedCommandClasses.Cast<object>())}");

                var manufacturerSpecific = multiSwitch.GetCommandClass<ManufacturerSpecific>();
                var manufacturerSpecificReport = await manufacturerSpecific.Get();
                LogMessage($"Manufacturer specific report of Node {manufacturerSpecificReport.Node:D3} is [{manufacturerSpecificReport}]");

                var association = multiSwitch.GetCommandClass<Association>();
                var associations = await association.Get();
                LogMessage($"Associations Get [{Encoding.Default.GetString(associations)}]");

                Subscribe(multiSwitch);

                MessageBox.Show("Done");
            }
            catch (AggregateException ex)
            {
                foreach (var inner in ex.InnerExceptions)
                {
                    LogMessage($"{inner}");
                }
            }
            catch (Exception ex)
            {
                LogMessage($"{ex}");
            }
        }

        private void LogMessage(string message)
        {
            var text = $"{DateTime.Now.TimeOfDay} {message}";

            lock (typeof(File))
            {
                File.AppendAllText(@"ZWave.log", text + Environment.NewLine);
            }
        }

        private void Subscribe(Node node)
        {
            var basic = node.GetCommandClass<Basic>();
            basic.Changed += (_, e) => LogMessage($"Basic report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var sensorMultiLevel = node.GetCommandClass<SensorMultiLevel>();
            sensorMultiLevel.Changed += (_, e) => LogMessage($"SensorMultiLevel report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var meter = node.GetCommandClass<Meter>();
            meter.Changed += (_, e) => LogMessage($"Meter report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var alarm = node.GetCommandClass<Alarm>();
            alarm.Changed += (_, e) => LogMessage($"Alarm report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var sensorBinary = node.GetCommandClass<SensorBinary>();
            sensorBinary.Changed += (_, e) => LogMessage($"SensorBinary report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var sensorAlarm = node.GetCommandClass<SensorAlarm>();
            sensorAlarm.Changed += (_, e) => LogMessage($"SensorAlarm report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var wakeUp = node.GetCommandClass<WakeUp>();
            wakeUp.Changed += (_, e) => { LogMessage($"WakeUp report of Node {e.Report.Node:D3} changed to [{e.Report}]"); };

            var switchBinary = node.GetCommandClass<SwitchBinary>();
            switchBinary.Changed += (_, e) => LogMessage($"SwitchBinary report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var multiChannel = node.GetCommandClass<MultiChannel>();
            multiChannel.Changed += (_, e) => LogMessage($"MultiChannel report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var switchMultiLevel = node.GetCommandClass<SwitchMultiLevel>();
            switchMultiLevel.Changed += (_, e) => LogMessage($"SwitchMultiLevel report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var thermostatSetpoint = node.GetCommandClass<ThermostatSetpoint>();
            thermostatSetpoint.Changed += (_, e) => LogMessage($"thermostatSetpoint report of Node {e.Report.Node:D3} changed to [{e.Report}]");

            var sceneActivation = node.GetCommandClass<SceneActivation>();
            sceneActivation.Changed += (_, e) => LogMessage($"sceneActivation report of Node {e.Report.Node:D3} changed to [{e.Report}]");
        }

        private async void AddAssoButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var association = multiSwitch.GetCommandClass<Association>();
                await association.Add(Convert.ToByte(GroupIdTextBox.Text), Convert.ToByte(NodeIdTextBox.Text));
                var associations = await association.Get();
                LogMessage($"Associations Get [{Encoding.Default.GetString(associations)}]");
                MessageBox.Show("Done");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private async void RemoveAssoButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var association = multiSwitch.GetCommandClass<Association>();
                await association.Remove(Convert.ToByte(GroupIdTextBox.Text), Convert.ToByte(NodeIdTextBox.Text));
                var associations = await association.Get();
                LogMessage($"Associations Get [{Encoding.Default.GetString(associations)}]");
                MessageBox.Show("Done");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }

    public class TmpTextWriter : TextWriter
    {
        private Action<string> callback;

        public TmpTextWriter(Action<string> callback)
        {
            this.callback = callback;
        }

        public override Encoding Encoding => Encoding.Default;

        public override void WriteLine(string value)
        {
            callback(value);
        }
    }
rpbrongers commented 6 years ago

I have a lot of multichannel devices (3 fibaro's, 2 greenwaves) and I never had to do anything with (multichannel)associations for them. But, of course, things may have changed in newer modules; mine are a few years old already. I see that one of the things I added in my code, is a Logline inside the Message class that spits out the raw bytes that were received (and also one for the bytes about to be sent). This will definitely show whether there are no bytes received at all. Unfortunately the Log is not available in that class, so you can either pass it through; hack the Channel to make the log method static and call those from within the Message code (add the logline after the line that reads 'await stream.ReadAsyncExact(buffer, 2, length);' I simply log all bytes in decimal format (Log.Debug($"<- : {string.Join(" ", buffer.AsEnumerable())}"));

I don't know how code-savvy you are, but alternatively, you can run the zwavecontroller.exe test tool and see if packets come in (bottom right section, both tabs) when you press the physical buttons (don't bother fiddling with the left and top-right sections, things should just work after you've configured the proper com-port).

What you would be looking for is a specific byte sequence after pressing a button. Unfortunately I am not a home to physically press a button, but when I query the switches endpoints (call the multichannel get for endpoint 1 and 2, see Bonus below), I get this (the '6' is the nodeId, would be 3 in your case): 1 13 0 4 0 6 7 96 13 1 1 37 3 0 248 1 13 0 4 0 6 7 96 13 2 1 37 3 0 191

Without explaining all of it, here's the bottom line: disregard the last byte (that's a checksum), and spot the only other byte that is different in both lines. See it? That's the endpoint id. Pressing the 'main' switch gives endpoint 1, the other one reports 2. Now, I can't really see more changes I did to make it work than are in this repo (but I might be wrong). So: if you do see this sequence coming in (in the Message class or with the other tool), it means that your physical switches send the proper stuff. If you see the same sequence coming in on either switch, you could perform a get on each endpoint to get the current status (see Bonus below). If the second switch doesn't send any bytes at all, then I don't know...no repro. Maybe the switch is faulty? Physically switching the light on and sending the bytes are two different things :-)

HTH, Reinhard

Bonus: add code to interactively get the status of a switch: Add this method to the Fibaro multiswitch class, and call it from outside.

public async Task GetStatus(byte endpointId)
        {
            if (endpointId > 2) throw new ArgumentOutOfRangeException(nameof(endpointId));
            await Node.GetCommandClass<MultiChannel>().Get(endpointId);
        }
BlueBasher commented 6 years ago

I've added the low level logging in the Message class and the plot thickens... Using your GetStatus code I get similar Byte arrays responses as you do: 1 13 0 4 0 3 7 96 13 1 1 37 3 0 185 1 13 0 4 0 3 7 96 13 2 1 37 3 0 186

When I physically switch the light off (or on) I do get bytes for both switches. But, I get different bytes for the main and the secondary switches.. 😕 For the main I get Basic, SwitchBinary, SwitchMultiLevel and Meter report data. But for the secondary switch I only get Basic reports.

When I request the status, I do see a difference in the CallbackID the controller is receiving. Doubt that this is causing the issue.

These are the logs I get when requesting the status or switching the switch. ZWave - Switch Off1.txt ZWave - Switch Off2.txt ZWave - GetStatus1.txt ZWave - GetStatus2.txt

rpbrongers commented 6 years ago

Interesting....Switch multi level? For a relay? That made me google: https://www.domoticz.com/forum/viewtopic.php?t=13209

It might be that you have that one, while I definitely have older ones (mine also don't have a power meter) All in all, it seems there's nothing really wrong, except for weird reports coming in. So the code needsto be changed to accommodate for that. I would simply remove the command classes you don't need from the Fibaro class, so they don't fire events. In fact, in my codebase, I removed the node ctor that adds all command classes and added the ones I know and need to the respective device classes Much more predictable.

If only the secondary switch is giving you troubles, I would, in the Fibaro class, react to a basic report by asking the status for switch 2 and fire a changed event if it was different than the last known value. (Sidenote: I don't have the 'changed' events myself, since I didn't need them and wanted a stateless design. I just publish to my message bus whatever state the node responds, and other interested microservices deal with their own state handling if they require it)

If I would have the same version switch I would be able to investigate further and possibly come with a better solution. Still wondering about that MultiLevel command, because that's for dimmers. I'm curious if the node really reports it as supported or controlled command class. In my codebase I added NIF support, so for new nodes I can see what command classes they support (and self configure the nodes). The zwavecontroller tool can also be used to find out about that.

Callbackid is merely a correlation byte that you need to increase on each call that requires it. But that's only for req/resp stuff. Unsolicited application updates (such as after physically flipping a switch, or periodic meter reports) do not have that.

Finally, when I'm in position to flip switches again (Sunday evening most likely), I will see what happens on the wire in my setup.

Cheers, Reinhard

Verzonden vanaf mijn Samsung Galaxy-smartphone. -------- Oorspronkelijk bericht -------- Van: Marco van Kimmenade notifications@github.com Datum: 21-07-17 20:19 (GMT+01:00) Aan: roblans/ZWave4Net ZWave4Net@noreply.github.com Cc: rpbrongers rpbrongers@hotmail.com, Author author@noreply.github.com Onderwerp: Re: [roblans/ZWave4Net] Fixed fibaro multiswitch reports (#11)

I've added the low level logging in the Message class and the plot thickens... Using your GetStatus code I get similar Byte arrays responses as you do: 1 13 0 4 0 3 7 96 13 1 1 37 3 0 185 1 13 0 4 0 3 7 96 13 2 1 37 3 0 186

When I physically switch the light off (or on) I do get bytes for both switches. But, I get different bytes for the main and the secondary switches.. 😕 For the main I get Basic, SwitchBinary, SwitchMultiLevel and Meter report data. But for the secondary switch I only get Basic reports.

When I request the status, I do see a difference in the CallbackID the controller is receiving. Doubt that this is causing the issue.

These are the logs I get when requesting the status or switching the switch. ZWave - Switch Off1.txthttps://github.com/roblans/ZWave4Net/files/1166395/ZWave.-.Switch.Off1.txt ZWave - Switch Off2.txthttps://github.com/roblans/ZWave4Net/files/1166397/ZWave.-.Switch.Off2.txt ZWave - GetStatus1.txthttps://github.com/roblans/ZWave4Net/files/1166398/ZWave.-.GetStatus1.txt ZWave - GetStatus2.txthttps://github.com/roblans/ZWave4Net/files/1166396/ZWave.-.GetStatus2.txt

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/roblans/ZWave4Net/pull/11#issuecomment-317075350, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AOia9sXfqmZTeYdEHsCj17YxG7aaD52Wks5sQOuFgaJpZM4N172j.

rpbrongers commented 6 years ago

OK, I promised an update after being able to physically flipping switches myself. Here's my lowdown: Flipping the second switch produces a nice multichannel report on the wire. Flipping the first switch produces only a BinarySwitch message. My code internally forwards the binaryswitch report as an endpoint 1 change (as if it was a multichannel originally), while the multichannel message is handled as usual (note: no other eventhandlers are active!). This is the same 'trick' they use in the OpenHAB codebase (and I'm sure also in the OpenZwave code). Apparently some devices exhibit similar behavior using the basic class.

Based on the logfiles you presented, you're out of luck, because there's no unique report type for node 2, so you can't use the same trick. However, this might work (assuming you have indeed a Fibaro Double Switch-2 (FGS-223): (please see: http://manuals.fibaro.com/switch-2/ , near bottom of page) It seems that using config parameter 31,32, 35,36 you can control the value that is reported when S1 or S2 is flipped on or off. By assigning unique values (e.g. set 35 to 1 and 36 to 254, you can recognize the report values in the basic report as coming from S2 and act accordingly. You need to set the association groups as well, otherwise you'll not get the value (I think, but you might try with only the lifeline first) Whenever I need to change device parameters, I always use the zwavecontroller.exe

BlueBasher commented 6 years ago

Thanks for having a look at what your switch is sending. I have forked this repo and made some changes to accommodate for the way my switch (indeed FGS-223) is sending events. It is working now but it may be not the most optimal solution, it uses more communication than strictly required. You're solution with the config changes would also be a good solution which uses less additional communication between controller and switch. So I think I'll go and implement that instead.

Again, thanks for your help and explanations!! Much appreciated!