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) |