Skip to content

Instantly share code, notes, and snippets.

@mattludwigs
Last active May 15, 2020 23:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattludwigs/b172eaae0831f71df5ab53e2d6066081 to your computer and use it in GitHub Desktop.
Save mattludwigs/b172eaae0831f71df5ab53e2d6066081 to your computer and use it in GitHub Desktop.

Grizzly v0.9.0 is an almost complete rewrite of Grizzly. It has been a massive overhaul that started nearly 6 months go and I am very excited about this release. Over the years we have learned a lot Z-Wave, Elixir, and OTP and with this refactor we were able to build a more reliable, robust, and consistent solution.

Update Grizzly

{:grizzly, "~> 0.9.0-rc.0"}

Runtime and On Start Notification Change

By default, Grizzly will start and manage thezipgateway binary automatically and once we have established a good connection to the Z-Wave controller we will let you know that Grizzly is ready to start handling Z-Wave commands. This mechanism is still part of Grizzly but we have changed the configuration of it.

You will no longer get a push from Grizzly when it is ready. Now, you can specify a module function args in the runtime config:

config :grizzly,
  runtime: [
    on_ready: {MyApp, :zwave_ready, []}
  ]

The configuration field for run_zipgaeway_bin has been moved into the :runtime configuration

config :grizzly,
  runtime: [
    run_zipgateway_bin: false
  ]

But this only useful if you don't want Grizzly supervising the zipgateway binary.

A bare minume working config now looks like:

config :grizzly,
  runtime: [
    on_ready: {MyApp, :a_function_my_app_module, []}
  ],
  serail_port: <serial device>

See the Grizzly README for more information about configuration options.

Send Command

The main function of Grizzly is Grizzly.send_command/3. It took many different arguments and returned many different things. The send_comamnd looks like this:

Grizzly.send_command(node_id, command :: atom(), command_args :: keyword(), command_opts :: keyword())

This function no longer takes a Grizzly.Node as that structure was deleted. The first argument now is always a node id.

The second argument use to be a module, but now we have moved to use atoms and controlling more of the implementation details of each command. To see the commands we support and the parameters they take you can the Grizzly.ZWave.Commands namespace.

The third argument says the same, just a keyword list of command parameters. This defaults to an empty keyword list.

The last argument is the command options. Most notably you can pass retires and timeouts in this field.

As a concrete example, let us assume I have a binary switch (on/off switch) on my network and is node id 3:

iex> Grizzly.send_command(3, :switch_binary_get)
%Grizzly.ZWave.Command{...}
iex> Grizzly.send_command(3, :switch_binary_set, value: :on)
:ok

Now I can add some command options to send_command

iex> Grizzly.send_command(3, :switch_binary_get, [], retries: 5)
%Grizzly.ZWave.Command{...}
iex> Grizzly.send_command(3, :switch_binary_set, [value: :off], timeout: 1_000)

Update Return Value From send_command

The return value was a map, but is now a Grizzly.ZWave.Command data structure. This provides a more consistent return value while giving some dynamic nature for each command.

The command structure has a :pramas field which is used to store the dynamic parameters for each command. This can be accessed via the Grizzly.ZWave.Command.param/2 or Grizzly.ZWave.Command.param!/2 functions.

iex> return_command = Grizzly.send_command(3, :switch_binary_get)
iex> return_command
%Grizzly.ZWave.Command{
  ...
  params: [value: :off],
  ...
}
iex> Grizzly.ZWave.Command.param!(return_command, :value)
:on
iex> Grizzly.ZWave.Command.param(return_command, :not_there)
nil

Adding and Removing a Device

The functions:

  • Grizzly.add_node
  • Grizzly.remove_node
  • Grizzly.add_node_stop
  • Grizzly.remove_node_stop

Have all moved to Grizzly.Inclusions module, so you can update like so:

alias Grizzly.Inclusions

...
Inclusions.add_node
...

The messages you receive from an inclusion now follow this pattern:

{:grizzly, :inclusion, %Grizzly.ZWave.Command{}}

We also don't automatically assign associations during inclusion anymore as not all applications desire for that to happen the way we dictate and we have opted to give the consumer more flexibility on how that should work. If you still want to get your devices talking to your hub after inclusion is complete you can call the new function: Grizzly.Node.set_lifeline_association.

For more information on some new features around inclusions see Grizzly.Inclusuions module docs.

Other Moved and Removed Grizzly Functions

Removed APIs

  • Grizzly.Node struct
  • Grizzly.Conn module
  • Grizzly.Notifications module
  • Grizzly.Packet module
  • Grizzly.close_connection
  • Grizzly.command_class_versions_known?
  • Grizzly.update_command_class_versions
  • Grizzly.start_learn_mode
  • Grizzly.get_command_class_version
  • Grizzly.has_command_class
  • Grizzly.connected?
  • Grizzly.has_command_class_names
  • Grizzly.config
  • Grizzly.Network.busy?
  • Grizzly.Network.ready?
  • Grizzly.Network.get_state
  • Grizzly.Network.set_state
  • Grizzly.Network.get_node
  • Grizzly.Node.new
  • Grizzly.Node.update
  • Grizzly.Node.put_ip
  • Grizzly.Node.get_ip
  • Grizzly.Node.connect
  • Grizzly.Node.disconnect
  • Grizzly.Node.make_config
  • Grizzly.Node.has_command_class?
  • Grizzly.Node.connected?
  • Grizzly.Node.command_class_names
  • Grizzly.Node.update_command_class_versions
  • Grizzly.Node.get_command_class_version
  • Grizzly.Node.command_class_version_known?
  • Grizzly.Node.update_command_class
  • Grizzly.Node.put_association
  • Grizzly.Node.get_association_list
  • Grizzly.Node.configure_association
  • Grizzly.Node.get_network_information
  • Grizzly.Node.initialize_command_versions

Moved APIs

  • Grizzly.reset_controller -> Grizzly.Network.reset_controller
  • Grizzly.get_nodes -> Grizzly.Network.get_node_ids
  • Grizzly.get_node_info -> Grizzly.Node.get_node_info
  • Grizzly.Notifications.subscribe -> Grizzly.subscribe_command and Grizzly.subscribe_commands
  • Grizzly.Notifications.unsubscribe -> Grizzly.unsubscribe
  • Grizzly.add_node -> Grizzly.Inclusions.add_node
  • Grizzly.remove_node -> Grizzly.Inclusions.remove_node
  • Grizzly.add_node_stop -> Grizzly.Inclusions.add_node_stop
  • Grizzly.remove_node_stop -> Grizzly.Inclusions.remove_node_stop
  • Grizzly.Client -> Grizzly.Transport
  • Grizzly.Security -> Grizzly.ZWave.Security
  • Grizzly.DSK -> Grizzly.ZWave.DSK
  • Grizzly.Node.add_lifeline_group -> Grizzly.Node.set_lifeline_association

All the commands that removed can be handled via send_command directly or using Elixir standard library functions.

Node is Removed

The node data structure is removed, and updating anywhere you pattern match %Grizzly.Node{} will have to be updated. If you trying to do something particular with a node you can use send_command with the node id to accomplish that.

Subscription to Events

We removed Grizzly.Notifications and provide there functions for working with getting events from the Z-Wave network:

  • Grizzly.subscribe_command/1
  • Grizzly.subscribe_commands/1
  • Grizzly.unsubscribe_command/1

The notifications of Grizzly < 0.9.0 used a custom notification topic, but 0.9.0 and greater you subscribe to a command or commands.

Old:

Grizzly.subscribe(:unsolicated_message)

This would just dump all messages from Z-Wave to the process that is listening for the incoming messages. However, we learned that often times processes just are wanting a few commands and the rest are greatly ignored.

Update to:

Grizzly.subscribe_command(:switch_binary_report)

Then when you handle the message you should expect this pattern:

{:grizzly, :event, <node_id>, %Grizzly.Command{...}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment