3 releases

0.1.2 May 15, 2020
0.1.1 Dec 9, 2019
0.1.0 Dec 8, 2019

#533 in HTTP server

46 downloads per month
Used in rustmo-devices

MIT license

36KB
707 lines

Rustmo - Talk to Your Code with Rust!

Rustmo is a library that allows for the creation of virtual devices which can be controlled (turned on or off) via Alexa-enabled things, such as the Amazon Echo Dot.

Rustmo is based on Fauxmo, and like Fauxmo, essentially emulates Belkin WeMo devices, backed by Rust code.

Rustmo simply emulates power "plugs". As such, virtual devices only have two states: On and Off.

Behind the scenes, Rustmo creates a SSDP listener along with a webserver for each VirtualDevice. Rustmo takes care of the details and all you need to do is implement the VirtualDevice trait to respond to Alexa requests.

Dependency

Declare a dependency on rustmo-server:

[dependencies]
rustmo-server = "0.1.0"

Implement the VirtualDevice trait

Secondly, create a struct that represents your device. Your "device" can be 100% pure code, or maybe it's a wrapper around a proprietary network protocol that controls a physical (but non-Alexa compatible) device somewhere on your network.

For examples of those, see the rustmo-devices crate for a few VirtualDeviceimplementations that attempt to control a Sony receiver, a Sony projector, and an Oppo DVD player.

Below is a simple, code-only example:

use rustmo_server::virtual_device::{VirtualDevice, VirtualDeviceError, VirtualDeviceState};

struct MyDevice {
    state: VirtualDeviceState,
}

impl MyDevice {
    fn new() -> Self {
        MyDevice{
            state: VirtualDeviceState::Off
        }
    }
}

impl VirtualDevice for MyDevice {
    fn turn_on(&mut self) -> Result<VirtualDeviceState, VirtualDeviceError> {
        eprintln!("Turning on");
        self.state = VirtualDeviceState::On;
        Ok(self.state)
    }

    fn turn_off(&mut self) -> Result<VirtualDeviceState, VirtualDeviceError> {
        eprintln!("Turning off");
        self.state = VirtualDeviceState::Off;
        Ok(self.state)
    }

    fn check_is_on(&mut self) -> Result<VirtualDeviceState, VirtualDeviceError> {
        eprintln!("Checking state");
        Ok(self.state)
    }
}

Create a RustmoServer, add a device

Next you need to create a RustmoServer instance. It needs to listen on whichever network interface your Alexa device is also connected.

Once created, you can add one or more VirtualDevice implementations, which makes them immediately discoverable by Alexa. And once discovered, immediately controllable.

use std::net::Ipv4Addr;
use std::str::FromStr;
use std::thread;

use rustmo_server::RustmoServer;

use crate::example::*;

fn main() -> std::io::Result<()> {
    // create the rustmo server and start listening for SSDP discovery requests 
    let mut server = RustmoServer::new(Ipv4Addr::from_str("192.168.0.100").unwrap());

    // add "My Device", making it controllable via Alexa on the specified port (1100)
    let _my_device = server.add_device("My Device", 1100, MyDevice::new())?;

    // wait forever
    thread::park();
    Ok(())
}

Various other helper methods exist for adding devices that need to poll for their state when changed, need to lie about their state when changed, combining devices into groups, etc.

Adding a VirtualDevice returns a WrappedVirtualDevice, which is your device instance wrapped in an Arc<Mutex<Box<dyn VirtualDevice>>>. This is the same type instance that RustmoServer internally uses to respond to Alexa requests. You're free to use this in other VirtualDevice implementations that, perhaps, form more complex operations across multiple devices and be guaranteed of thread safety when controlling the underlying device.

Note that each device needs a unique port number on which to listen for Alexa requests.

Talk to your VirtualDevice via Alexa

Now that you've started a Rustmo server and added a device, you first need to ask Alexa to discover new devices.

Simply say: Alexa, discover devices

About 45 seconds later, Alexa should have found your device named "My Device".

Now you can ask Alexa to turn it on or off via commands like

  • Alexa, turn My Device on
  • Alexa, turn my device off

Note that the only commands Alexa knows how to send to Rustmo devices (which, again, are emulated Belkin WeMo power plugs) are 'On' and 'Off'. It's not possible to ask Alexa to do something specific with the device, such as "change channel" or "tell me the weather". These things need to happen via Alexa Skills, which is outside the scope of this project.

Some VirtualDevice Implementation Notes

When you ask Alexa to turn a device on or off, it expects a response within 5 seconds. If Alexa doesn't receive a response in that amount of time it considers the device to be "not responding".

When a device's state is changed, Alexa immediately queries the device for its state (via VirtualDevice's check_is_on() method). If the response to that query doesn't match what Alexa expects, Alexa will report that the device "is malfunctioning".

Motivation

The motivation behind this project is to allow me to lose all the remote controls for my in-home movie theater.

Being able to turn everything on & off with one voice command, along with controlling other device-specific features via a custom mobile app, is a major qualify-of-life improvement for my 1%-er life.

Licensing

This code is licensed under the MIT license. Copyright 2019, ZomboDB, LLC.

Dependencies

~25MB
~526K SLoC