diff --git a/switchbot_ros/CMakeLists.txt b/switchbot_ros/CMakeLists.txt
index 20160ff79..cac584f7d 100644
--- a/switchbot_ros/CMakeLists.txt
+++ b/switchbot_ros/CMakeLists.txt
@@ -14,6 +14,11 @@ add_message_files(
FILES
Device.msg
DeviceArray.msg
+ Meter.msg
+ PlugMini.msg
+ Hub2.msg
+ Bot.msg
+ StripLight.msg
)
add_action_files(
diff --git a/switchbot_ros/launch/switchbot.launch b/switchbot_ros/launch/switchbot.launch
index 7e5a69e0b..cb9a68a96 100644
--- a/switchbot_ros/launch/switchbot.launch
+++ b/switchbot_ros/launch/switchbot.launch
@@ -2,6 +2,9 @@
+
+
+
@@ -10,4 +13,16 @@
secret: $(arg secret)
+
+
+
+ token: $(arg token)
+ secret: $(arg secret)
+ device_name: $(arg pub_device_name)
+ rate: $(arg pub_status_rate)
+
+
+
diff --git a/switchbot_ros/msg/Bot.msg b/switchbot_ros/msg/Bot.msg
new file mode 100644
index 000000000..74955d462
--- /dev/null
+++ b/switchbot_ros/msg/Bot.msg
@@ -0,0 +1,10 @@
+string DEVICEMODE_PRESS = "pressMode"
+string DEVICEMODE_SWITCH = "switchMode"
+string DEVICEMODE_CUSTOMIZE = "customizeMode"
+
+Header header # timestamp
+
+float64 battery # the current battery level, 0-100
+
+bool power # ON/OFF state True/False
+string device_mode # pressMode, switchMode, or customizeMode
diff --git a/switchbot_ros/msg/Hub2.msg b/switchbot_ros/msg/Hub2.msg
new file mode 100644
index 000000000..dfdf40b4f
--- /dev/null
+++ b/switchbot_ros/msg/Hub2.msg
@@ -0,0 +1,6 @@
+Header header # timestamp
+
+float64 temperature # temperature in celsius
+float64 humidity # humidity percentage
+
+int64 light_level # the level of illuminance of the ambience light, 1~20
diff --git a/switchbot_ros/msg/Meter.msg b/switchbot_ros/msg/Meter.msg
new file mode 100644
index 000000000..df731f552
--- /dev/null
+++ b/switchbot_ros/msg/Meter.msg
@@ -0,0 +1,5 @@
+Header header # timestamp
+
+float64 temperature # temperature in celsius
+float64 humidity # humidity percentage
+float64 battery # the current battery level, 0-100
diff --git a/switchbot_ros/msg/PlugMini.msg b/switchbot_ros/msg/PlugMini.msg
new file mode 100644
index 000000000..e74b320d0
--- /dev/null
+++ b/switchbot_ros/msg/PlugMini.msg
@@ -0,0 +1,7 @@
+Header header # timestamp
+
+float64 voltage # the voltage of the device, measured in Volt
+float64 weight # the power consumed in a day, measured in Watts
+float64 current # the current of the device at the moment, measured in Amp
+
+int32 minutes_day # he duration that the device has been used during a day, measured in minutes
diff --git a/switchbot_ros/msg/StripLight.msg b/switchbot_ros/msg/StripLight.msg
new file mode 100644
index 000000000..4164d184e
--- /dev/null
+++ b/switchbot_ros/msg/StripLight.msg
@@ -0,0 +1,8 @@
+Header header # timestamp
+
+bool power # ON/OFF state True/False
+
+int64 brightness # the brightness value, range from 1 to 100
+int64 color_r # Red color value 0-255
+int64 color_g # Green color value 0-255
+int64 color_b # Blue color value 0-255
\ No newline at end of file
diff --git a/switchbot_ros/scripts/control_switchbot.py b/switchbot_ros/scripts/control_switchbot.py
index 1c04c2b73..3dede636e 100644
--- a/switchbot_ros/scripts/control_switchbot.py
+++ b/switchbot_ros/scripts/control_switchbot.py
@@ -9,7 +9,7 @@
devices = client.get_devices()
print(devices)
-client.control_device('pendant-light', 'turnOff')
+client.control_device('pendant-light', 'turnOn', wait=True)
-client.control_device('bot74a', 'turnOn')
+client.control_device('bot74a', 'turnOn', wait=True)
diff --git a/switchbot_ros/scripts/switchbot_status_publisher.py b/switchbot_ros/scripts/switchbot_status_publisher.py
new file mode 100644
index 000000000..6ed571728
--- /dev/null
+++ b/switchbot_ros/scripts/switchbot_status_publisher.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+
+import os.path
+from requests import ConnectionError
+import rospy
+from switchbot_ros.switchbot import SwitchBotAPIClient
+from switchbot_ros.switchbot import DeviceError, SwitchBotAPIError
+from switchbot_ros.msg import Meter, PlugMini, Hub2, Bot, StripLight
+
+
+class SwitchBotStatusPublisher:
+ """
+ Publissh your switchbot status with ROS and SwitchBot API
+ """
+ def __init__(self):
+ # SwitchBot configs
+ # '~token' can be file path or raw characters
+ token = rospy.get_param('~token')
+ if os.path.exists(token):
+ with open(token) as f:
+ self.token = f.read().replace('\n', '')
+ else:
+ self.token = token
+
+ # Switchbot API v1.1 needs secret key
+ secret = rospy.get_param('~secret', None)
+ if secret is not None and os.path.exists(secret):
+ with open(secret, 'r', encoding='utf-8') as f:
+ self.secret = f.read().replace('\n', '')
+ else:
+ self.secret = secret
+
+ # Initialize switchbot client
+ self.bots = self.get_switchbot_client()
+ self.print_apiversion()
+
+ # Get parameters for publishing
+ self.rate = rospy.get_param('~rate', 0.1)
+ rospy.loginfo('Rate: ' + str(self.rate))
+
+ device_name = rospy.get_param('~device_name')
+ if device_name:
+ self.device_name = device_name
+ else:
+ rospy.logerr('No Device Name')
+ return
+
+ self.device_type = None
+ self.device_list = sorted(
+ self.bots.device_list,
+ key=lambda device: str(device.get('deviceName')))
+ for device in self.device_list:
+ device_name = str(device.get('deviceName'))
+ if self.device_name == device_name:
+ self.device_type = str(device.get('deviceType'))
+
+ if self.device_type:
+ rospy.loginfo('deviceName: ' + self.device_name + ' / deviceType: ' + self.device_type)
+ else:
+ rospy.logerr('Invalid Device Name: ' + self.device_name)
+ return
+
+ topic_name = '~' + self.device_name
+ topic_name = topic_name.replace('-', '_')
+
+ # Publisher Message Class for each device type
+ if self.device_type == 'Remote':
+ rospy.logerr('Device Type: "' + self.device_type + '" has no status in specifications.')
+ return
+ else:
+ if self.device_type == 'Meter':
+ self.msg_class = Meter
+ elif self.device_type == 'MeterPlus':
+ self.msg_class = Meter
+ elif self.device_type == 'WoIOSensor':
+ self.msg_class = Meter
+ elif self.device_type == 'Hub 2':
+ self.msg_class = Hub2
+ elif self.device_type == 'Plug Mini (JP)':
+ self.msg_class = PlugMini
+ elif self.device_type == 'Plug Mini (US)':
+ self.msg_class = PlugMini
+ elif self.device_type == 'Bot':
+ self.msg_class = Bot
+ elif self.device_type == 'Strip Light':
+ self.msg_class = StripLight
+ else:
+ rospy.logerr('No publisher process for "' + self.device_type + '" in switchbot_status_publisher.py')
+ return
+
+ self.status_pub = rospy.Publisher(topic_name, self.msg_class, queue_size=1, latch=True)
+
+ rospy.loginfo('Ready: SwitchBot Status Publisher for ' + self.device_name)
+
+
+ def get_switchbot_client(self):
+ try:
+ client = SwitchBotAPIClient(token=self.token, secret=self.secret)
+ rospy.loginfo('Switchbot API Client initialized.')
+ return client
+ except ConnectionError: # If the machine is not connected to the internet
+ rospy.logwarn_once('Failed to connect to the switchbot server. The client would try connecting to it when subscribes the ActionGoal topic.')
+ return None
+
+
+ def spin(self):
+ rate = rospy.Rate(self.rate)
+ while not rospy.is_shutdown():
+ rate.sleep()
+ if self.bots is None:
+ self.bots = self.get_switchbot_client()
+
+ if self.device_type == 'Remote':
+ return
+ else:
+ status = self.get_device_status(device_name=self.device_name)
+
+ if status:
+ time = rospy.get_rostime()
+ if self.msg_class == Meter:
+ msg = Meter()
+ msg.header.stamp = time
+ msg.temperature = status['temperature']
+ msg.humidity = status['humidity']
+ msg.battery = status['battery']
+ elif self.msg_class == Hub2:
+ msg = Hub2()
+ msg.header.stamp = time
+ msg.temperature = status['temperature']
+ msg.humidity = status['humidity']
+ msg.light_level = status['lightLevel']
+ elif self.msg_class == PlugMini:
+ msg = PlugMini()
+ msg.header.stamp = time
+ msg.voltage = status['voltage']
+ msg.weight = status['weight']
+ msg.current = status['electricCurrent']
+ msg.minutes_day = status['electricityOfDay']
+ elif self.msg_class == Bot:
+ msg = Bot()
+ msg.header.stamp = time
+ msg.battery = status['battery']
+ if status['power'] == 'on':
+ msg.power = True
+ else:
+ msg.power = False
+ msg.device_mode = status['deviceMode']
+ elif self.msg_class == StripLight:
+ msg = StripLight()
+ msg.header.stamp = time
+ if status['power'] == 'on':
+ msg.power = True
+ else:
+ msg.power = False
+ msg.brightness = status['brightness']
+ rgb_string = status['color']
+ r, g, b = map(int, rgb_string.split(':'))
+ msg.color_r = r
+ msg.color_g = g
+ msg.color_b = b
+ else:
+ return
+
+ if msg:
+ self.status_pub.publish(msg)
+
+
+ def get_device_status(self, device_name=None):
+ if self.bots is None:
+ return
+ elif device_name:
+ status = self.bots.device_status(device_name=device_name)
+ return status
+ else:
+ return
+
+
+ def print_apiversion(self):
+ if self.bots is None:
+ return
+
+ apiversion_str = 'Using SwitchBot API ';
+ apiversion_str += self.bots.api_version;
+ rospy.loginfo(apiversion_str)
+
+
+if __name__ == '__main__':
+ try:
+ rospy.init_node('switchbot_status_publisher')
+ ssp = SwitchBotStatusPublisher()
+ ssp.spin()
+ except rospy.ROSInterruptException:
+ pass
diff --git a/switchbot_ros/src/switchbot_ros/switchbot_ros_client.py b/switchbot_ros/src/switchbot_ros/switchbot_ros_client.py
index cd1b8a8d8..df1a7b00a 100644
--- a/switchbot_ros/src/switchbot_ros/switchbot_ros_client.py
+++ b/switchbot_ros/src/switchbot_ros/switchbot_ros_client.py
@@ -9,7 +9,8 @@ class SwitchBotROSClient(object):
def __init__(self,
actionname='switchbot_ros/switch',
- topicname='switchbot_ros/devices'):
+ topicname='switchbot_ros/devices',
+ timeout=5):
self.actionname = actionname
self.topicname = topicname
@@ -17,6 +18,8 @@ def __init__(self,
actionname,
SwitchBotCommandAction
)
+ rospy.loginfo("Waiting for action server to start. (timeout: " + str(timeout) + "[sec])")
+ self.action_client.wait_for_server(timeout=rospy.Duration(timeout,0))
def get_devices(self, timeout=None):