Information
We are currently investigating an issue with the editor of some pages. Please save your work and avoid to create new pages until this banner is gone.
To control a smart bulb using ACS we will implement a python component using the Component Generator tool.
First, it can be useful to have in mind the device specifications and properties available through the Tuya API Explorer:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{
"result": {
"category": "dj",
"functions": [
{
"code": "switch_led",
"desc": "{}",
"name": "开关",
"type": "Boolean",
"values": "{}"
},
{
"code": "work_mode",
"desc": "{\"range\":[\"white\",\"colour\",\"scene\",\"music\"]}",
"name": "工作模式",
"type": "Enum",
"values": "{\"range\":[\"white\",\"colour\",\"scene\",\"music\"]}"
},
{
"code": "bright_value_v2",
"desc": "{\"min\":10,\"max\":1000,\"scale\":0,\"step\":1}",
"name": "亮度",
"type": "Integer",
"values": "{\"min\":10,\"max\":1000,\"scale\":0,\"step\":1}"
},
{
"code": "temp_value_v2",
"desc": "{\"min\":0,\"max\":1000,\"scale\":0,\"step\":1}",
"name": "色温",
"type": "Integer",
"values": "{\"min\":0,\"max\":1000,\"scale\":0,\"step\":1}"
},
{
"code": "colour_data_v2",
"desc": "{\"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}}",
"name": "彩光模式值",
"type": "Json",
"values": "{\"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}}"
},
{
"code": "scene_data_v2",
"desc": "{\"scene_num\":{\"min\":1,\"scale\":0,\"max\":8,\"step\":1},\"scene_units\": {\"unit_change_mode\":{\"range\":[\"static\",\"jump\",\"gradient\"]},\"unit_switch_duration\":{\"min\":0,\"scale\":0,\"max\":100,\"step\":1},\"unit_gradient_duration\":{\"min\":0,\"scale\":0,\"max\":100,\"step\":1},\"bright\":{\"min\":0,\"scale\":0,\"max\":1000,\"step\":1},\"temperature\":{\"min\":0,\"scale\":0,\"max\":1000,\"step\":1},\"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}}}",
"name": "场景模式值",
"type": "Json",
"values": "{\"scene_num\":{\"min\":1,\"scale\":0,\"max\":8,\"step\":1},\"scene_units\": {\"unit_change_mode\":{\"range\":[\"static\",\"jump\",\"gradient\"]},\"unit_switch_duration\":{\"min\":0,\"scale\":0,\"max\":100,\"step\":1},\"unit_gradient_duration\":{\"min\":0,\"scale\":0,\"max\":100,\"step\":1},\"bright\":{\"min\":0,\"scale\":0,\"max\":1000,\"step\":1},\"temperature\":{\"min\":0,\"scale\":0,\"max\":1000,\"step\":1},\"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}}}"
},
{
"code": "countdown_1",
"desc": "{\"unit\":\"s\",\"min\":0,\"max\":86400,\"scale\":0,\"step\":1}",
"name": "倒计时",
"type": "Integer",
"values": "{\"unit\":\"s\",\"min\":0,\"max\":86400,\"scale\":0,\"step\":1}"
},
{
"code": "music_data",
"desc": "{\"change_mode\":{\"range\":[\"direct\",\"gradient\"]}, \"bright\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"temperature\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}",
"name": "音乐灯模式值",
"type": "Json",
"values": "{\"change_mode\":{\"range\":[\"direct\",\"gradient\"]}, \"bright\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"temperature\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
},
{
"code": "control_data",
"desc": "{\"change_mode\":{\"range\":[\"direct\",\"gradient\"]}, \"bright\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"temperature\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}",
"name": "调节模式值",
"type": "Json",
"values": "{\"change_mode\":{\"range\":[\"direct\",\"gradient\"]}, \"bright\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"temperature\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
},
{
"code": "rhythm_mode",
"desc": "{\"maxlen\":255}",
"name": "生物节律",
"type": "Raw",
"values": "{\"maxlen\":255}"
},
{
"code": "sleep_mode",
"desc": "{\"maxlen\":255}",
"name": "入睡",
"type": "Raw",
"values": "{\"maxlen\":255}"
},
{
"code": "wakeup_mode",
"desc": "{\"maxlen\":255}",
"name": "唤醒",
"type": "Raw",
"values": "{\"maxlen\":255}"
},
{
"code": "cycle_timing",
"desc": "{}",
"name": "循环定时",
"type": "Raw",
"values": "{}"
},
{
"code": "random_timing",
"desc": "{}",
"name": "随机定时",
"type": "Raw",
"values": "{}"
}
],
"status": [
{
"code": "switch_led",
"name": "开关",
"type": "Boolean",
"values": "{}"
},
{
"code": "work_mode",
"name": "模式",
"type": "Enum",
"values": "{\"range\":[\"white\",\"colour\",\"scene\",\"music\"]}"
},
{
"code": "bright_value_v2",
"name": "亮度",
"type": "Integer",
"values": "{\"min\":10,\"max\":1000,\"scale\":0,\"step\":1}"
},
{
"code": "temp_value_v2",
"name": "冷暖",
"type": "Integer",
"values": "{\"min\":0,\"max\":1000,\"scale\":0,\"step\":1}"
},
{
"code": "colour_data_v2",
"name": "彩光模式",
"type": "Json",
"values": "{\"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}}"
},
{
"code": "scene_data_v2",
"name": "情景模式",
"type": "Json",
"values": "{\"scene_num\":{\"min\":1,\"scale\":0,\"max\":8,\"step\":1},\"scene_units\": {\"unit_change_mode\":{\"range\":[\"static\",\"jump\",\"gradient\"]},\"unit_switch_duration\":{\"min\":0,\"scale\":0,\"max\":100,\"step\":1},\"unit_gradient_duration\":{\"min\":0,\"scale\":0,\"max\":100,\"step\":1},\"bright\":{\"min\":0,\"scale\":0,\"max\":1000,\"step\":1},\"temperature\":{\"min\":0,\"scale\":0,\"max\":1000,\"step\":1},\"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}}}"
},
{
"code": "countdown_1",
"name": "倒计时",
"type": "Integer",
"values": "{\"unit\":\"s\",\"min\":0,\"max\":86400,\"scale\":0,\"step\":1}"
},
{
"code": "music_data",
"name": "音乐灯调节",
"type": "Json",
"values": "{\"change_mode\":{\"range\":[\"direct\",\"gradient\"]}, \"bright\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"temperature\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
},
{
"code": "control_data",
"name": "调节控制",
"type": "Json",
"values": "{\"change_mode\":{\"range\":[\"direct\",\"gradient\"]}, \"bright\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"temperature\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}, \"h\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
},
{
"code": "rhythm_mode",
"name": "生物节律",
"type": "Raw",
"values": "{\"maxlen\":\"255\"}"
},
{
"code": "sleep_mode",
"name": "入睡",
"type": "Raw",
"values": "{\"maxlen\":\"255\"}"
},
{
"code": "wakeup_mode",
"name": "唤醒",
"type": "Raw",
"values": "{\"maxlen\":\"255\"}"
},
{
"code": "cycle_timing",
"name": "循环定时",
"type": "Raw",
"values": "{}"
},
{
"code": "random_timing",
"name": "随机定时",
"type": "Raw",
"values": "{}"
}
]
},
"success": true,
"t": 1652899017490,
"tid": "7f51aed8d6d911ecbae1dacb608a44da"
} |
For simplicity we will only use the "switch_led" property which allows us to turn the light on and off, but of course you can expand the functionality to control more properties.
Code Block | ||
---|---|---|
| ||
python -m pip install tinytuya |
The component generation tool uses a template in YML to create the component's IDL and implementation.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
working_dir: /home/developer/workspace
prefix: ACSIOT
module: acsiot
component_name: TuyaBulb
functions:
- 'void turnOn(in string api_region, in string api_key, in string api_secret, in string device_id)'
- 'void turnOff(in string api_region, in string api_key, in string api_secret, in string device_id)' |
The "working_dir" is where the component's IDL and implementation directories will be created, the rest is kind of self explanatory .
In short, the component generator tool works as follows:
Code Block | ||
---|---|---|
| ||
> python create_acs_component.py --help
Usage: create_acs_component.py [OPTIONS] COMPONENT_YAML_PATH
Creates an ACS component from a YAML definition.
Options:
-l, --language [python|cpp|java]
Component's implementation language.
[default: python]
--version Show the version and exit.
--help Show this message and exit. |
So in our case we will run:
Code Block | ||
---|---|---|
| ||
> python create_acs_component.py -l python path/to/tuya_bulb.yml |
If everything is ok the generator will create the component's IDL and implementation directories and files. Also it will add the component to the CDB. The output will be something like:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
2022-05-16 19:09:15 acs __main__[1225] INFO Loading component's YAML definition.
2022-05-16 19:09:15 acs __main__[1225] DEBUG Component's definition: {'working_dir': '/home/developer/workspace', 'prefix': 'ACSIOT', 'module': 'acsiot', 'component_name': 'TuyaBulb', 'functions': ['void turnOn(string api_region, string api_key, string api_secret, string device_id)', 'void turnOff(string api_region, string api_key, string api_secret, string device_id)']}
2022-05-16 19:09:15 acs __main__[1225] INFO Creating IDL's directories
\nCreating/checking Module directory
CREATED >>> |---idlTuyaBulb
CREATED >>> |---bin
CREATED >>> |---include
CREATED >>> |---idl
CREATED >>> |---lib
CREATED >>> |---lib/endorsed
CREATED >>> |---lib/python
CREATED >>> |---lib/ACScomponents
CREATED >>> |---lib/python/site-packages
CREATED >>> |---man
CREATED >>> |---man/man1
CREATED >>> |---man/man2
CREATED >>> |---man/man3
CREATED >>> |---man/man4
CREATED >>> |---man/man5
CREATED >>> |---man/man6
CREATED >>> |---man/man7
CREATED >>> |---man/man8
CREATED >>> |---man/mann
CREATED >>> |---man/manl
CREATED >>> |---doc
CREATED >>> |---object
CREATED >>> |---LOGS
CREATED >>> |---test
CREATED >>> |---src
CREATED >>> |---rtai
CREATED >>> |---config
CREATED >>> |---config/CDB
CREATED >>> |---config/CDB/schemas
CREATED >>> |---ChangeLog
\nCopying Makefile template for WS code
2022-05-16 19:09:15 acs __main__[1225] INFO Creating IDL's content
2022-05-16 19:09:15 acs __main__[1225] DEBUG IDL's content: #ifndef _TUYABULB_IDL_
#define _TUYABULB_IDL_
#pragma prefix "ACSIOT"
#include <acscomponent.idl>
module acsiot {
interface TuyaBulb : ACS::ACSComponent {
void turnOn(string api_region, string api_key, string api_secret, string device_id);
void turnOff(string api_region, string api_key, string api_secret, string device_id);
};
};
#endif
2022-05-16 19:09:15 acs __main__[1225] INFO Editing Makefile
2022-05-16 19:09:15 acs __main__[1225] INFO Adding component to CDB
2022-05-16 19:09:15 acs __main__[1225] DEBUG CDB's content: <?xml version="1.0" encoding="ISO-8859-1"?>
<Components xmlns="urn:schemas-cosylab-com:Components:1.0"
xmlns:cdb="urn:schemas-cosylab-com:CDB:1.0"
xmlns:baci="urn:schemas-cosylab-com:BACI:1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<e Name="PBEND_B_01" Code="acsexmplPowerSupplyImpl"
Type="IDL:alma/PS/PowerSupply:1.0"
Container="bilboContainer" ImplLang="cpp" />
...
...
...
<e Name="TuyaBulbPython"
Code="acsiotImpl.TuyaBulbImpl"
Type="IDL:ACSIOT/acsiot/TuyaBulb:1.0"
Container="aragornContainer" ImplLang="py" />
</Components>
2022-05-16 19:09:15 acs create_python_component[1225] INFO Creating component's directories.
\nCreating/checking Module directory
|---pyTuyaBulb
|---bin
|---include
|---idl
|---lib
|---lib/endorsed
|---lib/python
|---lib/ACScomponents
|---lib/python/site-packages
|---man
|---man/man1
|---man/man2
|---man/man3
|---man/man4
|---man/man5
|---man/man6
|---man/man7
|---man/man8
|---man/mann
|---man/manl
|---doc
|---object
|---LOGS
|---test
|---src
|---rtai
|---config
|---config/CDB
|---config/CDB/schemas
|---ChangeLog
\nCopying Makefile template for WS code
2022-05-16 19:09:15 acs create_python_component[1225] INFO Creating component's content.
2022-05-16 19:09:15 acs create_python_component[1225] DEBUG Component's content: # Client stubs and definitions, such as structs, enums, etc.
import acsiot
# Skeleton infrastructure for server implementation
import acsiot__POA
# Base component implementation
from Acspy.Servants.ACSComponent import ACSComponent
# Services provided by the container to the component
from Acspy.Servants.ContainerServices import ContainerServices
# Basic component lifecycle (initialize, execute, cleanUp and aboutToAbort methods)
from Acspy.Servants.ComponentLifecycle import ComponentLifecycle
class TuyaBulbImpl(acsiot__POA.TuyaBulb, ACSComponent, ContainerServices, ComponentLifecycle):
def __init__(self):
ACSComponent.__init__(self)
ContainerServices.__init__(self)
self._logger = self.getLogger()
def turnOn(self, api_region, api_key, api_secret, device_id):
raise NotImplementedError("This function should do something")
def turnOff(self, api_region, api_key, api_secret, device_id):
raise NotImplementedError("This function should do something")
2022-05-16 19:09:15 acs create_python_component[1225] INFO Editing Makefile |
In order to have a working component in valid python we will have to modify the boilerplate code as follows:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
# Client stubs and definitions, such as structs, enums, etc.
import acsiot
# Skeleton infrastructure for server implementation
import acsiot__POA
# Base component implementation
from Acspy.Servants.ACSComponent import ACSComponent
# Services provided by the container to the component
from Acspy.Servants.ContainerServices import ContainerServices
# Basic component lifecycle (initialize, execute, cleanUp and aboutToAbort methods)
from Acspy.Servants.ComponentLifecycle import ComponentLifecycle
import tinytuya
tinytuya.set_debug(True)
class TuyaBulbImpl(acsiot__POA.TuyaBulb, ACSComponent, ContainerServices, ComponentLifecycle):
def __init__(self):
ACSComponent.__init__(self)
ContainerServices.__init__(self)
self._logger = self.getLogger()
def turnOn(self, api_region, api_key, api_secret, device_id):
client = tinytuya.Cloud(
apiRegion=api_region,
apiKey=api_key,
apiSecret=api_secret
)
commands = {
'commands': [{
'code': 'switch_led',
'value': True
}]
}
print("Turning on...")
result = client.sendcommand(device_id, commands)
print("Results\n:", result)
def turnOff(self, api_region, api_key, api_secret, device_id):
client = tinytuya.Cloud(
apiRegion=api_region,
apiKey=api_key,
apiSecret=api_secret
)
commands = {
'commands': [{
'code': 'switch_led',
'value': False
}]
}
print("Turning off...")
result = client.sendcommand(device_id, commands)
print("Results\n:", result)
|
Code Block | ||
---|---|---|
| ||
cd idlTuyaBulb/src
make all
make install |
Code Block | ||
---|---|---|
| ||
cd pyTuyaBulb/src
make all install |
In one terminal run:
Code Block | ||
---|---|---|
| ||
acsStop
acsStart |
In a second terminal run:
Code Block | ||
---|---|---|
| ||
acsStartContainer -py aragornContainer |
And in a third terminal run:
Code Block | ||
---|---|---|
| ||
from Acspy.Clients.SimpleClient import PySimpleClient
api_region = 'us'
api_key = 'my_api_key'
api_secret = 'my_api_secret'
device_id = 'my_device_id'
client = PySimpleClient()
bulb = client.getComponent("TuyaBulbPython")
bulb.turnOn(api_region, api_key, api_secret, device_id)
bulb.turnOff(api_region, api_key, api_secret, device_id) |