Mios binding EOL

I don’t have many devices, so this is of interest. I’ll do some searching myself, but if you have any useful links drop them here!

I haven’t used this in a long time, and I no longer have a Vera to test, but here’s one sample of controlling the Vera from OH. Set up an Item for what you want to control, then send the http get request to Vera appropriately.

Change the IP appropriately, and change the DeviceNum appropriately.

On the Vera side you can post updates to OH on the REST api. I think I may have some examples of that too, but it may take a little longer to dig up.

val VERA_SWITCH = "http://192.168.86.205:3480/data_request?id=action&output_format=xml&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget"

rule "Kitchen Light"
when
        Item Kitchen_Light received command
then
        // logInfo("Kitchen Light", "Kitchen Light command received: " + receivedCommand)
        if (receivedCommand == ON) {
                sendHttpGetRequest(VERA_SWITCH + "&DeviceNum=31&newTargetValue=1")
        } else if (receivedCommand == OFF) {
                sendHttpGetRequest(VERA_SWITCH + "&DeviceNum=31&newTargetValue=0")
        } else {
                logInfo("Kitchen Light", "Command not handled")
        }
end

I’d definitely be interested, thanks! No rush though.


For others following along, I’ve (eventually) found out that you can find the details and methods for all your attached devices by going to (swap the IP address for your Vera IP address):

http://192.168.1.91:3480/data_request?id=invoke

For me, this shows the following:

image

Clicking on device 4, the radiator, shows the following:

Now, I’m mostly interested in setting the temperature, so I click SetCurrentSetpoint (NewCurrentSetpoint), which shows the following:

image

Not too encouraging. However, when clicking on SetCurrentSetpoint (NewCurrentSetpoint) on the previous page, the URL which is used is:

http://192.168.1.91:3480/data_request?id=action&DeviceNum=4&serviceId=urn:upnp-org:serviceId:TemperatureSetpoint1_Heat&action=SetCurrentSetpoint&NewCurrentSetpoint=

You can see there is no value after NewCurrentSetpoint=. Adding a value in, such as 19, and trying again, now shows a valid XML response, and eventually the thermostat itself updates.

image

EDIT:
Just dumping some info so I don’t lose it:

To perform a valid action, you need id=action and action= in the URL.
To get a variable, you need id=variableget and Variable= (case sensitive!) in the URL.

Yeah, this is the api that I think the current binding talks to. I just don’t even come close to having the java skills to write something that talks to it.

To go from Vera to OH, I used a Lua function like the one below. At the time I left Vera, this was with AltUI, but presumably it works with the standard Vera. Create a scene and use the function below (or the guts of the function) in the Lua code for the scene to set the OH item as desired. Set up whatever triggers you would like for the scene to activate this.

Another method is simply to poll the Vera periodically to get values. I generally prefer push as shown below, but the poll could be fine for some uses.

I cannot test this as I no longer have a Vera, but it should be reasonably close to what is needed.

function setOpenHabItem(item, value)
  local http = require("socket.http")
  local ltn12 = require("ltn12")
  local path = "http://192.168.86.212:8080/rest/items/" .. item
  local payload = "0" -- default is to turn off
  payload = value

  luup.log("setOpenHabItem payload is " .. payload)

  local response_body = { }
    local res, code, response_headers, status = http.request
    {
      url = path,
      method = "POST",
      headers =
      {
        ["Content-Type"] = "text/plain",
        ["Content-Length"] = payload:len()
      },
      source = ltn12.source.string(payload),
      sink = ltn12.sink.table(response_body)
    }
    if res ~= 0 or status ~= 200 then
       luup.log("res is: " .. res .. " and status is: " .. status)
    end
end

2 Likes

Thanks for this @jswim788!

In the end I opted for the polling method, as this means I only need to maintain one system’s code (openHAB).

If this is useful for anyone else I wrote the below python classes to interact with my thermostat and boiler relay, and now use these classes in my Jython rules. Note: this is the first time I have ever written classes in any language, so use with an abundance of caution! Note that I am using the openHAB LogAction, so I guess it’s not quite a standalone python class…

import requests
from core.actions import LogAction

vera_ip = "192.168.1.91"
vera_port = "3480"

class Vera:
    def __init__(self):
        self.ipaddress = vera_ip
        self.port = vera_port
        self.DeviceNum = 1
        self.ZWaveNetworkServiceId = "urn:micasaverde-com:serviceId:ZWaveNetwork1"

    def build_url(self, id, DeviceNum, serviceId, action):
        url = "http://{}:{}".format(self.ipaddress, self.port)
        url = url + "/data_request?"
        url = url + "id={}".format(id)
        url = url + "&DeviceNum={}".format(DeviceNum)
        url = url + "&serviceId={}".format(serviceId)
        url = url + "&{}".format(action)
        url = url + "&output_format=text"

        return str(url)

    def set_parameter(self, DeviceNum, serviceId, action_name, parameter_name, parameter_value):

        action = "action={}&{}={}".format(action_name, parameter_name, parameter_value)

        url = self.build_url("action", DeviceNum, serviceId, action)

        r = self.send_request(url)

        if r == "OK":
            return True
        else:
            return str(r)

    def get_parameter(self, DeviceNum, serviceId, parameter_name):

        action = "Variable={}".format(parameter_name)

        url = self.build_url("variableget", DeviceNum, serviceId, action)

        r = self.send_request(url)

        return str(r)

    def send_request(self, url):
        
        r = requests.get(url)

        return r.content

    def get_zwave_network_status(self):
        
        r = self.get_parameter(self.DeviceNum, self.ZWaveNetworkServiceId,"NetStatusText")

        return str(r.upper())

    def get_zwave_version(self):
        
        r = self.get_parameter(self.DeviceNum, self.ZWaveNetworkServiceId,"VersionInfo")

        return str(r.upper())
    
    def get_zwave_last_update(self):
        
        r = self.get_parameter(self.DeviceNum, self.ZWaveNetworkServiceId,"LastUpdate")

        return str(r.upper())


    class relay:
        def __init__(self):
            self.DeviceNum = 6
            self.SwitchServiceId = "urn:upnp-org:serviceId:SwitchPower1"
            self.HaDeviceServiceId = "urn:micasaverde-com:serviceId:HaDevice1"
            self.HVACModeServiceId = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1"
            self.HVACStateServiceId = "urn:micasaverde-com:serviceId:HVAC_OperatingState1"

        def set_switch_status(self, newTargetValue):
            r = Vera().set_parameter(self.DeviceNum, self.SwitchServiceId, "SetTarget", "newTargetValue", newTargetValue)

            if r == True:
                LogAction.logInfo("set_switch_status", "Switched to {}".format(newTargetValue))
            else:
                LogAction.logWarn("set_switch_status","Switch change failed. {}".format(r))

        def get_switch_status(self):
            r = Vera().get_parameter(self.DeviceNum, self.SwitchServiceId,"Status")

            #LogAction.logInfo("get_switch_status", "Switch status: {}".format(r))

            return str(r)
        
        def get_comms_status(self):

            r = Vera().get_parameter(self.DeviceNum, self.HaDeviceServiceId, "CommFailure")

            return int(r)

        def get_mode_status(self):

            r = Vera().get_parameter(self.DeviceNum, self.HVACModeServiceId,"ModeStatus")

            return str(r.upper())

        def get_mode_state(self):

            r = Vera().get_parameter(self.DeviceNum, self.HVACStateServiceId,"ModeState")

            return str(r.upper())

        def get_last_update(self):

            r = Vera().get_parameter(self.DeviceNum, self.HaDeviceServiceId,"LastUpdate")

            return str(r)

    class thermostat:
        def __init__(self):
            self.DeviceNum = 4
            self.SetpointServiceId = "urn:upnp-org:serviceId:TemperatureSetpoint1_Heat"
            self.SensorServiceId = "urn:upnp-org:serviceId:TemperatureSensor1"
            self.HaDeviceServiceId = "urn:micasaverde-com:serviceId:HaDevice1"
            self.HVACModeServiceId = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1"

        def set_setpoint(self, setpointTemp):

            r = Vera().set_parameter(self.DeviceNum, self.SetpointServiceId, "SetCurrentSetpoint", "NewCurrentSetpoint", setpointTemp)

            if r == True:
                LogAction.logInfo("set_setpoint", "Thermostat changed to {}".format(setpointTemp))
            else:
                LogAction.logWarn("set_setpoint","Thermostat change failed. {}".format(r))
        
        def get_current_setpoint(self):

            r = Vera().get_parameter(self.DeviceNum, self.SetpointServiceId,"CurrentSetpoint")

            #LogAction.logInfo("get_current_setpoint", "Current setpoint: {}".format(r))

            return str(r)

        def get_target_setpoint(self):

            r = Vera().get_parameter(self.DeviceNum, self.SetpointServiceId,"SetpointTarget")

            #LogAction.logInfo("get_target_setpoint", "Target setpoint: {}".format(r))

            return str(r)

        def get_current_temperature(self):

            r = Vera().get_parameter(self.DeviceNum, self.SensorServiceId,"CurrentTemperature")

            #LogAction.logInfo("get_current_temperature", "Current temperature: {}".format(r))

            return str(r)

        def get_battery_level(self):

            r = Vera().get_parameter(self.DeviceNum, self.HaDeviceServiceId,"BatteryLevel")

            #LogAction.logInfo("get_battery_level", "Battery level: {}".format(r))

            return str(r)

        def get_battery_level_last_update(self):

            r = Vera().get_parameter(self.DeviceNum, self.HaDeviceServiceId,"BatteryDate")

            return str(r)


        def get_last_update(self):

            r = Vera().get_parameter(self.DeviceNum, self.HaDeviceServiceId,"LastUpdate")

            return str(r)

        def get_mode_status(self):

            r = Vera().get_parameter(self.DeviceNum, self.HVACModeServiceId,"ModeStatus")

            return str(r.upper())

        def get_comms_status(self):

            r = Vera().get_parameter(self.DeviceNum, self.HaDeviceServiceId, "CommFailure")

            return int(r)

To use these classes within a rule I:

  • Saved the above into $OPENHAB_CONF/automation/lib/python/personal/heating_functions.py
  • Add the following to the top of any rules file which will use the classes:
import personal.heating_functions
reload(personal.heating_functions)
from personal.heating_functions import Vera
  • To set the thermostat setpoint in a rule, for example:
Vera().thermostat().set_setpoint(20)
  • To get the Z-Wave network status:
Vera().get_zwave_network_status()

EDIT 23/11/2020: Added more class methods.

I went for the following approach. I used MQTT too communicate between OH and VERA.

For the publishing of the device changes I use Vera2MQTT plugin on my VERA. This will generate Vera/Events for all device changes.
For changes invoked from OH side I created a Vera/Command topic which will be converted to https requests.

Here are the steps I took:

I already had a MQTT server running.

I installed Vera2MQTT plugin on my VERA. See https://community.getvera.com/t/mqtt-client-plugin/190658/77 for the details
Extended my MQTT bridge with all my zwave devices. See https://community.openhab.org/t/mios-binding-eol/107711/13 how to lookup the device numbers
An example:

mqtt.things file:

Thing topic vera-device-garden-light “Garden light” @ “VERA”
{
Channels:
Type switch : power “Power” [ stateTopic=“Vera/Events/12”, transformationPattern=“REGEX:(.“Status”.)∩JSONPATH:$.Status”, commandTopic=“Vera/Command/12/relay”, on=“1”, off=“0” ]
}

I also added a publish trigger to the MQTT broker to subscribe to all Vera/Command/# topics

Channels:
        Type publishTrigger : vera-mqtt "Trigger on vera commands" [ stateTopic="Vera/Command/#", separator="#" ]

vera.items file:

Switch Outside_Garden_Light “Garden light” { channel=“mqtt:topic:mosquitto:vera-device-garden-light:power” }

Create a rule to translate mqtt command to https request

vera.rules file:

rule "Translate VERA mqtt commands to http requests"
when 
      Channel "mqtt:broker:mosquitto:vera-mqtt" triggered
then
   var parts = receivedEvent.getEvent.toString.split("#")
   var topics = parts.get(0).toString.split("/")
   var payload = parts.get(1)
   var deviceNumber = topics.get(2)
   var commandType = topics.get(3)

   logInfo("vera", "Command received for dev: " + deviceNumber.toString() + ", type: " + commandType.toString() + ", payload: " + payload.toString())

   switch(commandType)
   {
      case "relay":
      {
            var url = "http://**<vera ip>**:3480/data_request?id=action&DeviceNum=" + deviceNumber + "&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=" + payload
            logInfo("mqtt", "TEST: " + url.toString())
            sendHttpGetRequest(url.toString())
      }
      case "dimmer":
      {
            var url = "http://**<vera ip>**:3480/data_request?id=action&DeviceNum=" + deviceNumber + "&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=" + payload
            logInfo("mqtt", "TEST: " + url.toString())
            sendHttpGetRequest(url.toString())
      }
   }
end

And at last removed the MIOS binding which prevented me to upgrade to OH3.

1 Like

The MQTT works well to publish updates. I’m trying to create a better way of sending data back to the Vera that doesn’t require a 1:1 configuration of deviceID and MQTT topic. The best idea I can come up with is caching a local copy of a mapping table between item/Vera name and its device ID that can be referenced via a rule, there is also a way of reading metadata (could be Vera deviceID) from items using jython. Has another else thought of a creative method of controlling multiple devices without having to code specific items?

More nonsense from me again!

I’m slowly testing OH3, and I think I’m going to control and monitor my Vera using the HTTP Binding, rather than the complicated setup I had previously.

I’m sticking with configuration files, so below are the Things and Items configurations that form a working concept for my small heating setup.

Things

//VERA
Thing http:url:vera "Vera" [
	baseURL = "http://192.168.1.91:3480/data_request?DeviceNum=1&serviceId=urn:micasaverde-com:serviceId:ZWaveNetwork1&output_format=text",
	refresh = "300",
	ignoreSSLErrors = "true"
]
{
	Channels:
		Type string: NetStatusText "Network Status" [
			mode = "READONLY",
			stateExtension = "&id=variableget&Variable=NetStatusText"
		]
		Type string: VersionInfo "Version Info" [
			mode = "READONLY",
			stateExtension = "&id=variableget&Variable=VersionInfo"
		]
		Type datetime: LastUpdate "Last Update" [
			mode = "READONLY",
			stateExtension = "&id=variableget&Variable=LastUpdate"
		]
}

//THERMOSTAT
Thing http:url:thermostat "Thermostat" [
	baseURL = "http://192.168.1.91:3480/data_request?DeviceNum=4&output_format=text",
	refresh = "60",
	ignoreSSLErrors = "true"
]
{
	Channels:
		Type number: CurrentSetpoint "Current Setpoint" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:upnp-org:serviceId:TemperatureSetpoint1_Heat&id=variableget&Variable=CurrentSetpoint"
		]
		Type number: SetpointTarget "Setpoint Target" [
			mode = "READWRITE",
			stateExtension = "&serviceId=urn:upnp-org:serviceId:TemperatureSetpoint1_Heat&id=variableget&Variable=SetpointTarget",
			commandExtension = "&serviceId=urn:upnp-org:serviceId:TemperatureSetpoint1_Heat&id=action&action=SetCurrentSetpoint&NewCurrentSetpoint=%2$s"
		]
		Type number: CurrentTemperature "Current Temperature" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:upnp-org:serviceId:TemperatureSensor1&id=variableget&Variable=CurrentTemperature"
		]
		Type number: BatteryLevel "Battery Level" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:micasaverde-com:serviceId:HaDevice1&id=variableget&Variable=BatteryLevel"
		]
		Type datetime: BatteryDate "Battery Date" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:micasaverde-com:serviceId:HaDevice1&id=variableget&Variable=BatteryDate"
		]
		Type datetime: LastUpdate "Last Update" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:micasaverde-com:serviceId:HaDevice1&id=variableget&Variable=LastUpdate"
		]
		Type string: ModeStatus "Mode Status" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:upnp-org:serviceId:HVAC_UserOperatingMode1&id=variableget&Variable=ModeStatus"
		]
		Type string: CommFailure "Comm Failure" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:micasaverde-com:serviceId:HaDevice1&id=variableget&Variable=CommFailure",
			stateTransformation = "MAP:vera2online.map"
		]
}

//RELAY
Thing http:url:relay "Relay" [
	baseURL = "http://192.168.1.91:3480/data_request?DeviceNum=6&output_format=text",
	refresh = "120",
	ignoreSSLErrors = "true"
]
{
	Channels:
		Type switch: Status "Relay Status" [
			mode = "READWRITE",
			stateExtension = "&serviceId=urn:upnp-org:serviceId:SwitchPower1&id=variableget&Variable=Status",
			commandExtension = "&serviceId=urn:upnp-org:serviceId:SwitchPower1&id=action&action=SetTarget&newTargetValue=%2$s",
			onValue = "1",
			offValue = "0"
		]
		Type string: CommFailure "Comm Failure" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:micasaverde-com:serviceId:HaDevice1&id=variableget&Variable=CommFailure",
			stateTransformation = "MAP:vera2online.map"
		]
		Type string: ModeStatus "Mode Status" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:upnp-org:serviceId:HVAC_UserOperatingMode1&id=variableget&Variable=ModeStatus"
		]
		Type string: ModeState "Mode State" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:micasaverde-com:serviceId:HVAC_OperatingState1&id=variableget&Variable=ModeState"
		]
		Type datetime: LastUpdate "Last Update" [
			mode = "READONLY",
			stateExtension = "&serviceId=urn:micasaverde-com:serviceId:HaDevice1&id=variableget&Variable=LastUpdate"
		]
}

Items

//VERA
String strNetStatusText "Z-Wave Network Status" <status> ["Status"] { channel="http:url:vera:NetStatusText" }
String strVersionInfo "Z-Wave Version Info" <status> ["Status"] { channel="http:url:vera:VersionInfo" }
DateTime dtVeraLastUpdate "Vera Last Update" <calendar> ["Status"] { channel="http:url:vera:LastUpdate" }

//THERMOSTAT
Group gThermostat (gGroundHallway) ["HVAC"]
Number nCurrentSetpoint "Thermostat Setpoint" <temperature>  ["Setpoint","Temperature"] { channel="http:url:thermostat:CurrentSetpoint"}
Number nSetpointTarget "Thermostat Setpoint Target" <temperature> (gThermostat) ["Setpoint","Temperature"] { channel="http:url:thermostat:SetpointTarget", stateDescription=""[min="15", max="21", pattern="%.0f"], widget="oh-slider-card"[min=15, max=21, step=1, scale=true], listWidget="oh-slider-item"[min=15, max=21, step=1, scale=true] }
Number nCurrentTemperature "Thermostat Current Temperature" <temperature> (gThermostat) ["Measurement","Temperature"] { channel="http:url:thermostat:CurrentTemperature" }
Number nBatteryLevel "Thermostat Battery Level" <battery> (gThermostat) ["LowBattery"] { channel="http:url:thermostat:BatteryLevel" }
DateTime dtBatteryDate "Thermostat Battery Date" <calendar> ["Status"] { channel="http:url:thermostat:BatteryDate" }
DateTime dtThermostatLastUpdate "Thermostat Last Update" <calendar> ["Status"] { channel="http:url:thermostat:LastUpdate" }
String strThermostatModeStatus "Thermostat Mode Status" <mode> ["Status"] { channel="http:url:thermostat:ModeStatus" }
String strThermostatCommFailure "Thermostat Comm Failure" <mode> ["Status"] { channel="http:url:thermostat:CommFailure" }

//RELAY
Switch sRelayStatus "Relay Status" <switch> ["Switch"] { channel="http:url:relay:Status" }
String strRelayCommFailure "Relay Comm Failure" <mode> ["Status"] { channel="http:url:relay:CommFailure" }
String strRelayModeStatus "Relay Mode Status" <mode> ["Status"] { channel="http:url:relay:ModeStatus" }
String strRelayModeState "Relay Mode State" <mode> ["Status"] { channel="http:url:relay:ModeState" }
DateTime dtRelayLastUpdate "Thermostat Last Update" <calendar> ["Status"] { channel="http:url:relay:LastUpdate" }

vera2online.map

0=Online
1=Offline
2=Offline
1 Like

Now we’re talking. I’m going to spin up an OH3 vm and try some of this.

It’s obviously easier if you use the UI to configure all this - far fewer spelling mistakes and issues with Things files not loading properly. However, I find it much faster using configuration files!

Yes, I’m 100% config files. I use source control and an IDE to work my automation system. Maybe it’s cause I started on 1.x but I just never could get into the inbox/paperui way of doing things.

Great start. I have it pulling info in. What do you have in your vera2online.map?

Have you worked with sending back to Vera? I see everything is set to READONLY in your examples.

Thanks!

Ah, I should have shared that:

0=Online
1=Offline
2=Offline

I lose some of the nuance with the simple online/offline, but I’m not bothered.

There’s definitely one READWRITE Channel in there (SetpointTarget), and there should be another but I made a mistake which I will correct (Status for the relay Thing).

I’m not looking for a lot of info out of the Vera. The stuff I was seeing come back already was mostly it. I watch for on/off, temps and such and the one magic one is last poll success as I use it and expire to monitor if a zwave device goes awol. Although, I’m thinking expire is a dead v1 binding too now isn’t it :frowning:

More testing to follow. THANKS!

It is, but the functionality has been pulled into the core for openHAB 3. Syntax is exactly the same, so it should work straight away for you!

Good to know. I hadn’t looked deeply into that yet as the mios binding was my show stopper. mios1 and expire1 were the two remaining legacy bindings in my setup.

I think your solution is the answer. It will take a bit of rework to convert my items over to using http but reality is it isn’t a whole lot worse than the mios binding if you didn’t use the item generator.

I was able to control an outlet and see read only data. I’ll mess with other items that I need to write back to like dimmers and such but I’m confident this will work.

Well several frustrating hours later.

I’m trying to get lastpollsuccess.

Where did you get your urls from? I tried to extrapolate from what you have.

I’m able to get lastpollsuccess with this in a browser:

http://w.x.y.z:3480/data_request?DeviceNum=76&output_format=text&serviceId=urn:micasaverde-com:serviceId:ZWaveNetwork1&id=variableget&Variable=LastPollSuccess

In my things I used:

        Type datetime: LastPollSuccess "Last Poll Success" [
            mode = "READONLY",
            stateExtension = "serviceId=urn:micasaverde-com:serviceId:ZWaveNetwork1&id=variableget&Variable=LastPollSuccess"
        ]

Which I somewhat pieced together using info at:

http://w.x.y.z:3480/data_request?id=lu_invoke&DeviceNum=76

I get errors in openhab.log saying it cannot find the URL and the url is definitely wrong. It isn’t made up of the baseURL and the extension URL:

Requesting ‘http://w.x.y.z:3480/data_request?DeviceNum=76&output_format=text&serviceId=urn:micasaverde-com:serviceId:ZWaveNetwork1&Variable=LastPollSuccess

Doesn’t make sense, it somehow is modifying the url.

Same as you: from the lu_invoke URL on my device, and the three links at the bottom of my previous post.

What does your baseURL look like? Note that my stateExtension URLs all start with & - if they didn’t then the binding might put in a / depending on how your baseURL ends. Per the documentation:

When concatenating the baseURL and stateExtension or commandExtension the binding checks if a proper URL part separator (/, & or ?) is present and adds a / if missing.

Note also that issues with properly loading Things files after they have saved are still a thing in OH3.

Either restart openHAB, or do this.

Yeah, I had been putting the “&”, must have mixed that up with that particular attempt. I checked this morning and I had the & in my thing. I restarted OH and it started working so I guess I was getting trolled by the loading issue. Thanks!