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" } |
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: ~/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): # raise NotImplementedError("This function should do something") client = tinytuya.Cloud( apiRegion=api_region, apiKey=api_key, apiSecret=api_secret ) commands = { 'commands': [{ 'code': 'switch_led', 'value': True }] } print("Sending command...") result = client.sendcommand(device_id, commands) print("Results\n:", result) def turnOff(self, api_region, api_key, api_secret, device_id): # raise NotImplementedError("This function should do something") client = tinytuya.Cloud( apiRegion=api_region, apiKey=api_key, apiSecret=api_secret ) commands = { 'commands': [{ 'code': 'switch_led', 'value': False }] } print("Sending command...") 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 = 'my_region' 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) |