Giter VIP home page Giter VIP logo

tuya / tuya-connector Goto Github PK

View Code? Open in Web Editor NEW
57.0 15.0 33.0 316 KB

tuya-connector helps you efficiently create cloud development projects regarding the OpenAPI or message subscription capabilities. You can put all the focus on business logic without taking care of server-side programming nor relational databases.

License: Apache License 2.0

Java 100.00%
openapi restful connector rest-api rest-client tuya tuya-api tuya-smart tuya-cloud iot

tuya-connector's Introduction

English | 中文版

License: Apache 2 Version: 1.0.0

tuya-connector helps you efficiently create cloud development projects regarding the OpenAPI or message subscription capabilities. You can put all the focus on business logic without taking care of server-side programming nor relational databases.

Quick start

Integrate Spring Boot

<dependency>
  <groupId>com.tuya</groupId>
  <artifactId>tuya-spring-boot-starter</artifactId>
  <version>#{latest.version}</version>
</dependency>

<!-- Specify the Maven repository URL -->
<repository>
    <id>tuya-maven</id>
    <url>https://maven-other.tuya.com/repository/maven-public/</url>
</repository>

Configuration

# ClientId & SecretKey generated on the Tuya Cloud Development Platform
connector.ak=***
connector.sk=***

region configuration

# region configuration(default region is China if without configuration)
# more details, please check: com.tuya.connector.open.common.constant.TuyaRegion)
connector.region=CN

Usage

Call OpenAPI operations
  1. Create the Connector interface, which is the mapping class of OpenAPI.
public interface DeviceConnector {
    /**
     * query device info by device_id
     * @param deviceId
     * @return
     */
    @GET("/v1.0/devices/{device_id}")
    Device getById(@Path("device_id") String deviceId);
}
  1. Set @ConnectorScan for the class of the Spring Boot application. You can set @EnableMessaging to enable the message subscription capability.

Note: Since the connector SDK relies on the reflection mechanism, and starting from JDK 9, a modularization mechanism has been introduced. Therefore, when starting, you need to add the parameters --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED to avoid potential errors.

@ConnectorScan(basePackages = "com.xxx.connectors")
@EnableMessaging
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  1. The Connector interface will be scanned and injected into the Spring container.
@Service
public class DeviceService {
    @Autowired
    private DeviceConnector device;

    public Device getById(String deviceId) {
        return device.getById(deviceId);
    }
}
Subscribe to message events
/**
 * device status data report event
 */
@EventListener
public void statusReportMessage(StatusReportMessage event) {
    log.info("### StatusReport event happened, eventInfo: {}", event);
}

How it works: implement extensions based on the connector framework.

Extension points of OpenAPI

  • ErrorProcessor

You can define the implementation class of ErrorProcessor to handle different error responses. For example, if a token expires, it can be automatically refreshed. The API operation will be tried again with the refreshed token. TokenInvalidErrorProcessor is the built-in implementation class of ErrorProcessor.

The extended ErrorProcessor must be injected into the Spring container to take effect.

  • ContextManager

The connector framework supports TuyaContextManager on which the automatic token refreshing depends. TuyaContextManager can prepare the context before API operations, and manage information including data source connection, tokens, and multilingual text.

  • TokenManager

TuyaTokenManager is the default token management mechanism and implements the TokenManager interface in the connector framework. The token information is cached on the premises.

To manage the token on the premises, you can extend TokenManager and inject it into the Spring container.

  • HeaderProcessor

TuyaHeaderProcessor implements the processing logic of the header for OpenAPI operations, including the required attribute values and signatures.

Extension points of messages

  • MessageDispatcher

TuyaMessageDispatcher implements MessageDispatcher interface for message dispatching in the connector framework. The dispatcher features message ordering and data decryption. It allows you to create specific message types and publish messages based on Spring's event mechanism.

  • MessageEvent

You can add ApplicationListener to listen for required events. The connector framework includes all the Tuya's message event types. The message data contains ciphertext messages and plaintext messages.

Message event BizCode Description
StatusReportMessage statusReport Report data to the cloud.
OnlineMessage online A device is online.
OfflineMessage offline A device is offline.
NameUpdateMessage nameUpdate Modify the device name.
DpNameUpdateMessage dpNameUpdate Modify the name of a data point.
DeleteMessage delete Remove a device.
BindUserMessage bindUser Bind the device to a user account.
UpgradeStatusMessage upgradeStatus The update status.
AutomationExternalActionMessage automationExternalAction Automate an external action.
SceneExecuteMessage sceneExecute Execute a scene.

tuya-connector's People

Contributors

chyern avatar fengkaijia avatar jialuo-ty avatar norman-maguangyao avatar q1763868326 avatar quinniup avatar tuya-qiufeng avatar vpavic avatar yuqiufeng avatar zhang-bigsmart avatar zhoujiaping avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tuya-connector's Issues

支不支持springboot kotlin ??

你好,请问 支不支持springboot kotlin ??
我在项目中折腾了一下午也不顺利,框架应该需要调整才能支持吧?

看了一下starter的网络请求内部实现,略微臃肿。

看了一下你们http通信采用的技术栈是 retrofit+okhttp,整体设计的思路是很清晰的,分层设计也到位。最终也是实现的代理interface实现http调用。 个人建议更加简洁轻量的实现:okhttp+spring-core(注解扫描工具)实现基于代理interface的网络请求。

@EnableMessage not working with failed to authenticate the client and unknown request id from server: -1

I am trying to enable event listener for the following events:

  • StatusReportMessage
  • OnlineMessage
  • OfflineMessage

But I am getting the following exception:

ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tuyaMessageDispatcher': Invocation of init method failed

ERROR 29922 --- [r-client-io-1-1] o.apache.pulsar.client.impl.ClientCnx : [id: 0x019e4ea1, L:/192.168.15.3:58736 ! R:mqe.tuyaus.com/54.149.50.50:7285] Failed to authenticate the client

WARN 29922 --- [r-client-io-1-1] o.apache.pulsar.client.impl.ClientCnx : [id: 0x019e4ea1, L:/192.168.15.3:58736 ! R:mqe.tuyaus.com/54.149.50.50:7285] Received unknown request id from server: -1

INFO 29922 --- [r-client-io-1-1] o.apache.pulsar.client.impl.ClientCnx : [id: 0x019e4ea1, L:/192.168.15.3:58736 ! R:mqe.tuyaus.com/54.149.50.50:7285] Disconnected

Any special reason why this is happening? Is there any Cloud Tuya Service that I need to enable? If so, what is the service and how about to indicate that in the README.md?

Java version: OpenJDK 20.
Tuya spring boot starter: 1.3.0

Thank you

调用控制设备接口报错:2009, error msg : clientId is invalid

2023-12-28 14:43:22.128 [asyncDeviceTaskExecutor-43] ERROR com.tuya.connector.api.core.ConnectorProxy-Error invoke connector[com.rocky.school.tuya.connectors.DeviceConnector.sendCommand(java.lang.String, com.rocky.school.tuya.model.request.SendCommandRequest), args: (6c6f7aba291346f38ejy6z, SendCommandRequest(id=null, ids=null, commands=[TuyaCommand(code=switch_2, value=false)]))]
2023-12-28 14:43:22.129 [asyncDeviceTaskExecutor-96] ERROR com.rocky.school.service.AsyncTaskManager-controlDevicea0fd2772-8fd8-4e18-92be-66335bbf1d3c
com.tuya.connector.api.exceptions.ConnectorResultException: error code : 2009, error msg : clientId is invalid
at com.tuya.connector.api.exceptions.ExceptionFactory.ofCode(ExceptionFactory.java:29)
at com.tuya.connector.api.core.delegate.RetrofitDelegate.execute(RetrofitDelegate.java:115)
at com.tuya.connector.api.core.ConnectorProxy.invoke(ConnectorProxy.java:51)
at com.sun.proxy.$Proxy173.sendCommand(Unknown Source)
at sun.reflect.GeneratedMethodAccessor334.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29)
at com.tuya.connector.api.error.ErrorProcessorInterceptor.intercept(ErrorProcessorInterceptor.java:29)
at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26)
at com.sun.proxy.$Proxy173.sendCommand(Unknown Source)
at sun.reflect.GeneratedMethodAccessor334.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29)
at com.tuya.connector.api.context.ContextInterceptor.intercept(ContextInterceptor.java:30)
at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26)
at com.sun.proxy.$Proxy173.sendCommand(Unknown Source)
at com.rocky.school.service.AsyncTaskManager.controlDevice(AsyncTaskManager.java:255)
at com.rocky.school.service.AsyncTaskManager$$FastClassBySpringCGLIB$$8cd7bed8.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

tuya-connector中的sample中的项目,报PulsarClientException

填写了
connector.ak=
connector.sk=
和connector.region=CN
项目启动的时候报错,还需要其他配置吗
**
2022-06-08 09:59:48.744 INFO 9510 --- [r-client-io-1-1] o.a.pulsar.client.impl.ConnectionPool : [[id: 0x28a8fc80, L:/192.168.1.6:55829 - R:mqe.tuyacn.com/121.5.96.165:7285]] Connected to server
2022-06-08 09:59:49.124 WARN 9510 --- [r-client-io-1-1] org.apache.pulsar.client.impl.ClientCnx : [id: 0x28a8fc80, L:/192.168.1.6:55829 - R:mqe.tuyacn.com/121.5.96.165:7285] Received error from server: Failed to authenticate
2022-06-08 09:59:49.125 WARN 9510 --- [r-client-io-1-1] org.apache.pulsar.client.impl.ClientCnx : [id: 0x28a8fc80, L:/192.168.1.6:55829 - R:mqe.tuyacn.com/121.5.96.165:7285] Received unknown request id from server: -1
2022-06-08 09:59:49.129 INFO 9510 --- [r-client-io-1-1] org.apache.pulsar.client.impl.ClientCnx : [id: 0x28a8fc80, L:/192.168.1.6:55829 ! R:mqe.tuyacn.com/121.5.96.165:7285] Disconnected
2022-06-08 09:59:49.132 WARN 9510 --- [r-client-io-1-1] o.a.pulsar.client.impl.ConnectionPool : [[id: 0x28a8fc80, L:/192.168.1.6:55829 ! R:mqe.tuyacn.com/121.5.96.165:7285]] Connection handshake failed: org.apache.pulsar.client.api.PulsarClientException: Connection already closed
2022-06-08 09:59:49.239 WARN 9510 --- [al-listener-3-1] o.a.pulsar.client.impl.PulsarClientImpl : [topic: persistent://cm9vww5******(此处手动打码)pzzfgw/out/event] Could not get connection while getPartitionedTopicMetadata -- Will try again in 100 ms
2022-06-08 09:59:49.275 INFO 9510 --- [r-client-io-1-1] o.a.pulsar.client.impl.ConnectionPool : [[id: 0x28441b2d, L:/192.168.1.6:55830 - R:mqe.tuyacn.com/121.5.96.165:7285]] Connected to server
2022-06-08 09:59:49.377 WARN 9510 --- [r-client-io-1-1] org.apache.pulsar.client.impl.ClientCnx : [id: 0x28441b2d, L:/192.168.1.6:55830 - R:mqe.tuyacn.com/121.5.96.165:7285] Received error from server: Failed to authenticate
**

TuyaTokenManager怎么注入

@Autowired
private TuyaTokenManager tokenManager;

Field tokenManager in cloud.obboiot.tuya.util.TuyaRequestUtil required a bean of type 'com.tuya.connector.open.api.token.TuyaTokenManager' that could not be found.

error code : 1109, error msg : 请求参数非法,请检查 t : 1692034588289

考虑以下请求:

POST /v1.0/devices/DEVICE_ID_HERE/commands HTTP/1.1
Host: openapi.tuyaus.com
client_id: ********
access_token: ********
sign: ********
t: 1692034308756
sign_method: HMAC-SHA256
Content-Type: application/json
Content-Length: 140

{
  "commands": [
    {
      "code": "switch_1",
      "value": true
    },
    {
      "code": "switch_2",
      "value": true
    }
  ]
}

如果我使用 postman 或 curl 执行此操作,一切正常,但使用 tuya-spring-boot-starter 我收到以下错误:

error code : 1109, error msg : 请求参数非法,请检查 t : 1692034588289

为什么会发生这种情况?

这是请求的代码:

public interface TuyaClient {

    @POST("/v1.0/devices/{device_id}/commands")
    Boolean commands(@Path("device_id") String deviceId, @Body String commandRequest);

}

[...]

public void switchDevice(String deviceId) {
    CommandRequest commandRequest = new CommandRequest();
    commandRequest.addCommand(new Command("switch_1", Boolean.TRUE));
    commandRequest.addCommand(new Command("switch_2", Boolean.TRUE));
     try {
         tuyaClient.commands(deviceId, JSON.toJSONString(commandRequest));

    } catch (JSONException | ConnectorResultException e) {
        e.printStackTrace();
    }
}

这是堆栈跟踪:

com.tuya.connector.api.exceptions.ConnectorResultException: error code : 1109, error msg : 请求参数非法,请检查 t : 1692034588289
	at com.tuya.connector.api.exceptions.ExceptionFactory.ofCode(ExceptionFactory.java:33)
	at com.tuya.connector.api.core.delegate.RetrofitDelegate.execute(RetrofitDelegate.java:115)
	at com.tuya.connector.api.core.ConnectorProxy.invoke(ConnectorProxy.java:50)
	at jdk.proxy4/jdk.proxy4.$Proxy142.commands(Unknown Source)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29)
	at com.tuya.connector.api.error.ErrorProcessorInterceptor.intercept(ErrorProcessorInterceptor.java:31)
	at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26)
	at jdk.proxy4/jdk.proxy4.$Proxy142.commands(Unknown Source)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29)
	at com.tuya.connector.api.context.ContextInterceptor.intercept(ContextInterceptor.java:30)
	at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26)
	at jdk.proxy4/jdk.proxy4.$Proxy142.commands(Unknown Source)
	at mypackage.platform.service.devices.DeviceService.switchDevice(DeviceService.java:38)
	at mypackage.platform.service.devices.DeviceController.switchDevice(DeviceController.java:41)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:676)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:1623)

Result(code=1109,msg=请求参数非法,请检查,t=1692034588289) is not successful for requeset: mypackage.clients.tuya.TuyaClient.commands(java.lang.String, java.lang.String), args: (DEVICE_ID_HERE, {"commands":[{"code":"switch_1","value":true},{"code":"switch_2","value":true}]})

我也尝试过,但也没有运气:

@POST("/v1.0/iot-03/devices/{device_id}/commands")
Boolean command(@Path("device_id") String deviceId, @Body Map<String, Object> commands);

有解决这个问题的线索吗?

在简单设备属性上从 Fastjson 设置属性错误

我正在使用 tuya-spring-boot-starter 版本 1.3.0 来查询设备详细信息,如下所示:

TuyaClient.java:

public interface TuyaClient {

    @GET("/v1.0/devices/{device_id}")
    Device getById(@Path("device_id") String deviceId);

}

Device.java:

@Data
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PUBLIC)
public class Device {
    String id;

    @JSONField(name="update_time")
    Long update_time;

    @JSONField(name="create_time")
    Long createTime;

    Boolean online;

    String icon;

    // other fields here
}

问题:

某些字段(例如“update_time”)没有被 Fastjson 反序列化,没有明确的原因,从而产生以下错误:

调用连接器时出错。 原因:com.alibaba.fastjson.JSONException:设置属性错误,mypackage.models.Device#update_time"

我想知道为什么会发生这种情况,因为例如,与“update_time”具有相同类型的字段“create_time”正在被正确反序列化。

有什么线索吗?

Java version: 20
tuya-spring-boot-starter: 1.3.0

bug-report : Error creating bean with name 'exportProxyInjector' defined ---Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [com.tuya.connector.spring.export.ExportProxyInjector] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]

devices: apple M1
jdk: openjdk version "11.0.11" 2021-04-20 LTS
pom-jdk: 1.8
描述:使用spring-boot-starter-webflux进行开发的时候, Failed to introspect Class [com.tuya.connector.spring.export.ExportProxyInjector] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]。使用spring-boot-starter-web正常。
spring-boot-starter-parent版本:2.5.1

log:


2021-06-21 21:24:48.245 ERROR 31829 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exportProxyInjector' defined in class path resource [com/tuya/connector/spring/boot/autoconfigure/ConnectorExportAutoConfiguration.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [com.tuya.connector.spring.export.ExportProxyInjector] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
	at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:64)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:338)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332)
	at acheng1314.cn.tuyadevices.TuyaDevicesApplicationKt.main(TuyaDevicesApplication.kt:17)
Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.tuya.connector.spring.export.ExportProxyInjector] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
	at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:739)
	at org.springframework.util.ReflectionUtils.doWithLocalFields(ReflectionUtils.java:671)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.buildResourceMetadata(CommonAnnotationBeanPostProcessor.java:366)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.findResourceMetadata(CommonAnnotationBeanPostProcessor.java:347)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(CommonAnnotationBeanPostProcessor.java:295)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1098)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576)
	... 15 common frames omitted
Caused by: java.lang.NoClassDefFoundError: Lorg/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping;
	at java.lang.Class.getDeclaredFields0(Native Method)
	at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
	at java.lang.Class.getDeclaredFields(Class.java:1916)
	at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:734)
	... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
	... 25 common frames omitted

查询项目下设备列表接口 含有下划线的字段解析失败

@Getter
@Setter
public class TuyaDeviceBO2 {

    /**
     * 设备 ID
     */
    private String id;

    /**
     * 设备的激活时间(时间戳秒数)
     */
    private Long active_time;

    /**
     * 设备的产品品类
     */
    private String category;

    /**
     * 设备的初次配网时间(时间戳秒数)
     */
    private Long create_time;

    /**
     * 设备的更新时间(时间戳秒数)
     */
    private Long update_time;

    /**
     * 设备的自定义名称
     */
    private String custom_name;

    /**
     * 设备的图标
     */
    private String icon;

    /**
     * 设备的 IP 地址
     */
    private String ip;

    /**
     * 设备的在线状态
     */
    private Boolean is_online;

    /**
     * 设备的纬度
     */
    private String lat;

    /**
     * 设备的局域网加密后的唯一密钥
     */
    private String local_key;

    /**
     * 设备的经度
     */
    private String lon;

    /**
     * 设备的名称
     */
    private String name;

    /**
     * 设备的产品 ID
     */
    private String product_id;

    /**
     * 设备的产品名称
     */
    private String product_name;

    /**
     * 是否为子设备
     */
    private Boolean sub;

    /**
     * 设备的时区
     */
    private String time_zone;

    /**
     * 设备的 UUID
     */
    private String uuid;
}

image
这个接口下划线的字段全部为空

@Service
public interface TuyaDeviceConnector {

    @GET("/v2.0/cloud/thing/{device_id}")
    TuyaDeviceBO2 getById(@Path("device_id") String deviceId);


    @GET("/v2.0/cloud/thing/device")
    List<TuyaDeviceBO2> getList(@Query("product_ids") String pid, @Query("page_size") int pageSize);

}

image

查询单个设备详情 是正常的
image

字段都是一样的 咋回事呢

tuya-spring-boot-starter-sample won't work due to baseUrl == null

I cloned the latest sample (using 1.1.1) and ran it:

Caused by: java.lang.NullPointerException: baseUrl == null
	at java.base/java.util.Objects.requireNonNull(Objects.java:246) ~[na:na]
	at retrofit2.Retrofit$Builder.baseUrl(Retrofit.java:505) ~[retrofit-2.9.0.jar:na]

The only thing I changed is specifying the properties:

connector.ak=...
connector.sk=...
connector.region=EU

It works again when I specify this manually:

connector.api.base-url=https://openapi-weaz.tuyaeu.com

Can't connect: Received unknown request id from server: -1

I'm getting this using the spring boot sample project (based on 1.1.1 or 1.1.0):


2021-12-08 20:47:01.577  INFO 23344 --- [r-client-io-1-1] o.a.pulsar.client.impl.ConnectionPool    : [[id: 0x00a610cb, L:/192.168.0.214:61818 - R:mqe.tuyaeu.com/3.120.66.229:7285]] Connected to server
2021-12-08 20:47:01.665  WARN 23344 --- [r-client-io-1-1] org.apache.pulsar.client.impl.ClientCnx  : [id: 0x00a610cb, L:/192.168.0.214:61818 - R:mqe.tuyaeu.com/3.120.66.229:7285] Received error from server: Failed to authenticate
2021-12-08 20:47:01.665  WARN 23344 --- [r-client-io-1-1] org.apache.pulsar.client.impl.ClientCnx  : [id: 0x00a610cb, L:/192.168.0.214:61818 - R:mqe.tuyaeu.com/3.120.66.229:7285] Received unknown request id from server: -1
2021-12-08 20:47:01.665  INFO 23344 --- [r-client-io-1-1] org.apache.pulsar.client.impl.ClientCnx  : [id: 0x00a610cb, L:/192.168.0.214:61818 ! R:mqe.tuyaeu.com/3.120.66.229:7285] Disconnected
2021-12-08 20:47:01.665  WARN 23344 --- [r-client-io-1-1] o.a.pulsar.client.impl.ConnectionPool    : [[id: 0x00a610cb, L:/192.168.0.214:61818 ! R:mqe.tuyaeu.com/3.120.66.229:7285]] Connection handshake failed: org.apache.pulsar.client.api.PulsarClientException: Connection already closed
2021-12-08 20:47:01.770  WARN 23344 --- [al-listener-3-1] o.a.pulsar.client.impl.PulsarClientImpl  : [topic: persistent://593yydhvb5jxeul1klsj/out/event] Could not get connection while getPartitionedTopicMetadata -- Will try again in 100 ms
2021-12-08 20:47:01.791  INFO 23344 --- [r-client-io-1-1] o.a.pulsar.client.impl.ConnectionPool    : [[id: 0x26369fea, L:/192.168.0.214:61819 - R:mqe.tuyaeu.com/3.120.66.229:7285]] Connected to server

This is what I specified in properties:

connector.ak=...
connector.sk=...
connector.api.base-url=https://openapi-weaz.tuyaeu.com
connector.region=EU

I double-checked the following:

  • AK and SK are correct: Calling the API using retrofit works
  • The messaging service is enabled and online for the App from the Access Key

com.tuya.connector.api.exceptions.ConnectorDelegateException: Error create retrofit delegate for connector

版本: <tuya-spring-boot-starter.version>1.3.5</tuya-spring-boot-starter.version>
jdk版本 21

具体报错如下:

`com.tuya.connector.api.exceptions.ConnectorDelegateException: Error create retrofit delegate for connector : com.tuya.open.spring.boot.sample.ability.api.DeviceConnector.
Cause: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @458c1321

at com.tuya.connector.api.core.delegate.DelegateFactory.forRetrofit(DelegateFactory.java:26)
at com.tuya.connector.api.core.ConnectorProxy.invoke(ConnectorProxy.java:46)
at jdk.proxy2/jdk.proxy2.$Proxy84.getDevices(Unknown Source)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29)
at com.tuya.connector.api.error.ErrorProcessorInterceptor.intercept(ErrorProcessorInterceptor.java:31)
at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26)
at jdk.proxy2/jdk.proxy2.$Proxy84.getDevices(Unknown Source)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29)
at com.tuya.connector.api.context.ContextInterceptor.intercept(ContextInterceptor.java:30)
at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26)
at jdk.proxy2/jdk.proxy2.$Proxy84.getDevices(Unknown Source)
at com.tuya.open.spring.boot.sample.DemoAplicationTest.getDevices(DemoAplicationTest.java:25)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @458c1321
at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:391)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:367)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:315)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:203)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:197)
at javassist.util.proxy.SecurityActions.setAccessible(SecurityActions.java:159)
at javassist.util.proxy.DefineClassHelper$JavaOther.defineClass(DefineClassHelper.java:213)
at javassist.util.proxy.DefineClassHelper$Java11.defineClass(DefineClassHelper.java:52)
at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:260)
at javassist.ClassPool.toClass(ClassPool.java:1232)
at javassist.CtClass.toClass(CtClass.java:1384)
at com.tuya.connector.api.core.delegate.RetrofitDelegate.deriveFrom(RetrofitDelegate.java:201)
at com.tuya.connector.api.core.delegate.RetrofitDelegate.(RetrofitDelegate.java:74)
at com.tuya.connector.api.core.delegate.DelegateFactory.forRetrofit(DelegateFactory.java:23)
... 18 more`

getById (as an example) not working with SpringBoot 3

Hi there,

I am trying to use the tuya-spring-boot-starter, but as a basic example of get a device by Id I have an error:

"Error create retrofit delegate for connector : com.mypackage.tuya.TuyaClient.
Cause: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @211fecaa

package com.mypackage.platform.clients.tuya;

import com.tuya.connector.api.annotations.Body;
import com.tuya.connector.api.annotations.GET;
import com.tuya.connector.api.annotations.POST;
import com.tuya.connector.api.annotations.Path;
import com.mypackage.platform.service.devices.models.Device;

import java.util.Map;

public interface TuyaClient {

    @GET("/v1.0/devices/{device_id}")
    Device getById(@Path("device_id") String deviceId);

}

Java version: OpenJDK 20
SpringBoot: 3.1.2
Tuya spring boot starter: 1.3.0

Any clue/suggestion of how to fix this are gratefully accepted. Thank you.

依赖的版本问题

依赖的版本要明确出来啊, 不然一直都是最新版本容易有兼容性问题

跟mybatis-spring1.3.0冲突

Cannot convert value of type 'com.tuya.connector.api.config.Configuration' to required type 'org.apache.ibatis.session.Configuration' for property 'configuration': no matching editors or conversion strategy found

mybatis-spring版本:

org.mybatis
mybatis-spring
1.3.0

I/O exception (java.net.SocketException) caught when processing request to {s}->https://openapi.tuyaeu.com:443: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

image

2023-11-02 06:53:18.041 INFO 23300 --- [ main] o.a.p.s.o.a.h.impl.execchain.RetryExec : I/O exception (java.net.SocketException) caught when processing request to {s}->https://openapi.tuyaeu.com:443: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
2023-11-02 06:53:18.042 INFO 23300 --- [ main] o.a.p.s.o.a.h.impl.execchain.RetryExec : Retrying request to {s}->https://openapi.tuyaeu.com:443
2023-11-02 06:53:52.040 INFO 23300 --- [ main] o.a.p.s.o.a.h.impl.execchain.RetryExec : I/O exception (java.net.SocketException) caught when processing request to {s}->https://openapi.tuyaeu.com:443: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

Anyone knows whats the issue? This happens at launch.
Is https://openapi.tuyaeu.com:443 the normal URL for EU data center?

java.lang.NoClassDefFoundError: retrofit2/Call

您好,我使用1.1.0版本的tuya-spring-boot-starter,调用接口的时候出现Caused by: java.lang.NoClassDefFoundError: retrofit2/Call
导致异常的是retrofit2.Retrofit#validateServiceInterface方法内的service.getDeclaredMethods()
此时service的值是com.xxxxx.ssssss.service.deviceprovider.tuya.ability.api.$DeviceConnector,也就是我定义的涂鸦接口
,service对象的类加载器为AppClassLoader

运行环境
JDK11
解压jar后依赖包都在
tuya-spring-boot-starter-1.1.0.jar
tuya-api-1.1.0.jar
tuya-messaging-1.1.0.jar
tuya-common-1.1.0.jar
retrofit-2.9.0.jar
okhttp-4.9.3.jar
okio-jvm-2.8.0.jar
gson-2.8.6.jar
converter-gson-2.9.0.jar

请问开发者是否遇到过类似的问题呢?

com.tuya.connector.api.exceptions.ConnectorException: Error invoke connector. Cause: java.lang.NoClassDefFoundError: retrofit2/Call
	at com.tuya.connector.api.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:21) ~[connector-api-1.1.1.jar!/:na]
	at com.tuya.connector.api.core.ConnectorProxy.invoke(ConnectorProxy.java:57) ~[connector-api-1.1.1.jar!/:na]
	at com.sun.proxy.$Proxy150.getStatusById(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29) ~[connector-api-1.1.1.jar!/:na]
	at com.tuya.connector.api.error.ErrorProcessorInterceptor.intercept(ErrorProcessorInterceptor.java:29) ~[connector-api-1.1.1.jar!/:na]
	at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26) ~[connector-api-1.1.1.jar!/:na]
	at com.sun.proxy.$Proxy150.getStatusById(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at com.tuya.connector.api.plugin.Invocation.proceed(Invocation.java:29) ~[connector-api-1.1.1.jar!/:na]
	at com.tuya.connector.api.context.ContextInterceptor.intercept(ContextInterceptor.java:30) ~[connector-api-1.1.1.jar!/:na]
	at com.tuya.connector.api.plugin.Plugin.invoke(Plugin.java:26) ~[connector-api-1.1.1.jar!/:na]
	at com.sun.proxy.$Proxy150.getStatusById(Unknown Source) ~[na:na]
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[na:na]
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
	at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290) ~[na:na]
	at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746) ~[na:na]
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) ~[na:na]
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) ~[na:na]
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) ~[na:na]
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) ~[na:na]
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) ~[na:na]
Caused by: java.lang.NoClassDefFoundError: retrofit2/Call
	at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
	at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3166) ~[na:na]
	at java.base/java.lang.Class.getDeclaredMethods(Class.java:2309) ~[na:na]
	at retrofit2.Retrofit.validateServiceInterface(Retrofit.java:187) ~[retrofit-2.9.0.jar!/:na]
	at retrofit2.Retrofit.create(Retrofit.java:141) ~[retrofit-2.9.0.jar!/:na]
	at com.tuya.connector.api.core.delegate.RetrofitDelegate.<init>(RetrofitDelegate.java:79) ~[connector-api-1.1.1.jar!/:na]
	at com.tuya.connector.api.core.delegate.DelegateFactory.forRetrofit(DelegateFactory.java:23) ~[connector-api-1.1.1.jar!/:na]
	at com.tuya.connector.api.core.ConnectorProxy.invoke(ConnectorProxy.java:47) ~[connector-api-1.1.1.jar!/:na]
	... 28 common frames omitted
Caused by: java.lang.ClassNotFoundException: retrofit2.Call
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) ~[na:na]
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[na:na]
	... 36 common frames omitted

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.