-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #505 from y-yosuke/add-switchbot-status-publisher
Add switchbot status publisher
- Loading branch information
Showing
10 changed files
with
255 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Header header # timestamp | ||
|
||
float64 temperature # temperature in celsius | ||
float64 humidity # humidity percentage | ||
float64 battery # the current battery level, 0-100 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters