├── .gitattributes ├── .gitignore ├── .travis.yml ├── composer.json ├── composer.lock ├── examples ├── callFunction.php ├── claimDevice.php ├── deleteDevice.php ├── deleteToken.php ├── deleteWebhook.php ├── getDeviceInfo.php ├── getDeviceInfoInProduct.php ├── getToken.php ├── getVariable.php ├── listDevices.php ├── listDevicesInProduct.php ├── listTokens.php ├── listWebhooks.php ├── newAccessToken.php ├── newWebhook.php ├── setDeviceName.php ├── signalDevice.php ├── tinker.bin ├── tinker.cpp └── uploadFirmware.php ├── phpParticle.class.php ├── phpParticle.config.sample.php ├── phpParticle.firmware.cpp ├── phpunit.xml ├── readme.md ├── src └── ParticleAPI.php └── tests └── ParticleAPITest.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the actual config file 2 | phpParticle.config.php 3 | vendor/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - hhvm 8 | 9 | before_script: 10 | - composer self-update 11 | - composer install --prefer-source --no-interaction --dev 12 | 13 | script: phpunit -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "articfox1986/phpparticle", 3 | "description": "A php package for working with the particle API", 4 | "license": "MIT", 5 | "keywords": ["particle","php"], 6 | "authors": [ 7 | { 8 | "name": "Devin Pearson", 9 | "email": "devin@blackhat.co.za" 10 | }, 11 | { 12 | "name": "Harrison Jones", 13 | "email": "harrison@hhj.me" 14 | } 15 | ], 16 | "require": {}, 17 | "require-dev": { 18 | "phpunit/phpunit": "4.0.*" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "articfox1986\\phpparticle\\": "src" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "3a855fa0bd364da6c971236f33c0f2e5", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "phpunit/php-code-coverage", 12 | "version": "2.0.17", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 16 | "reference": "c4e8e7725e351184a76544634855b8a9c405a6e3" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c4e8e7725e351184a76544634855b8a9c405a6e3", 21 | "reference": "c4e8e7725e351184a76544634855b8a9c405a6e3", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.3.3", 26 | "phpunit/php-file-iterator": "~1.3", 27 | "phpunit/php-text-template": "~1.2", 28 | "phpunit/php-token-stream": "~1.3", 29 | "sebastian/environment": "~1.0", 30 | "sebastian/version": "~1.0" 31 | }, 32 | "require-dev": { 33 | "ext-xdebug": ">=2.1.4", 34 | "phpunit/phpunit": "~4" 35 | }, 36 | "suggest": { 37 | "ext-dom": "*", 38 | "ext-xdebug": ">=2.2.1", 39 | "ext-xmlwriter": "*" 40 | }, 41 | "type": "library", 42 | "extra": { 43 | "branch-alias": { 44 | "dev-master": "2.0.x-dev" 45 | } 46 | }, 47 | "autoload": { 48 | "classmap": [ 49 | "src/" 50 | ] 51 | }, 52 | "notification-url": "https://packagist.org/downloads/", 53 | "license": [ 54 | "BSD-3-Clause" 55 | ], 56 | "authors": [ 57 | { 58 | "name": "Sebastian Bergmann", 59 | "email": "sb@sebastian-bergmann.de", 60 | "role": "lead" 61 | } 62 | ], 63 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 64 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 65 | "keywords": [ 66 | "coverage", 67 | "testing", 68 | "xunit" 69 | ], 70 | "time": "2015-05-25 05:11:59" 71 | }, 72 | { 73 | "name": "phpunit/php-file-iterator", 74 | "version": "1.3.4", 75 | "source": { 76 | "type": "git", 77 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 78 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" 79 | }, 80 | "dist": { 81 | "type": "zip", 82 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", 83 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", 84 | "shasum": "" 85 | }, 86 | "require": { 87 | "php": ">=5.3.3" 88 | }, 89 | "type": "library", 90 | "autoload": { 91 | "classmap": [ 92 | "File/" 93 | ] 94 | }, 95 | "notification-url": "https://packagist.org/downloads/", 96 | "include-path": [ 97 | "" 98 | ], 99 | "license": [ 100 | "BSD-3-Clause" 101 | ], 102 | "authors": [ 103 | { 104 | "name": "Sebastian Bergmann", 105 | "email": "sb@sebastian-bergmann.de", 106 | "role": "lead" 107 | } 108 | ], 109 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 110 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 111 | "keywords": [ 112 | "filesystem", 113 | "iterator" 114 | ], 115 | "time": "2013-10-10 15:34:57" 116 | }, 117 | { 118 | "name": "phpunit/php-text-template", 119 | "version": "1.2.1", 120 | "source": { 121 | "type": "git", 122 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 123 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 124 | }, 125 | "dist": { 126 | "type": "zip", 127 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 128 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 129 | "shasum": "" 130 | }, 131 | "require": { 132 | "php": ">=5.3.3" 133 | }, 134 | "type": "library", 135 | "autoload": { 136 | "classmap": [ 137 | "src/" 138 | ] 139 | }, 140 | "notification-url": "https://packagist.org/downloads/", 141 | "license": [ 142 | "BSD-3-Clause" 143 | ], 144 | "authors": [ 145 | { 146 | "name": "Sebastian Bergmann", 147 | "email": "sebastian@phpunit.de", 148 | "role": "lead" 149 | } 150 | ], 151 | "description": "Simple template engine.", 152 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 153 | "keywords": [ 154 | "template" 155 | ], 156 | "time": "2015-06-21 13:50:34" 157 | }, 158 | { 159 | "name": "phpunit/php-timer", 160 | "version": "1.0.7", 161 | "source": { 162 | "type": "git", 163 | "url": "https://github.com/sebastianbergmann/php-timer.git", 164 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" 165 | }, 166 | "dist": { 167 | "type": "zip", 168 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 169 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 170 | "shasum": "" 171 | }, 172 | "require": { 173 | "php": ">=5.3.3" 174 | }, 175 | "type": "library", 176 | "autoload": { 177 | "classmap": [ 178 | "src/" 179 | ] 180 | }, 181 | "notification-url": "https://packagist.org/downloads/", 182 | "license": [ 183 | "BSD-3-Clause" 184 | ], 185 | "authors": [ 186 | { 187 | "name": "Sebastian Bergmann", 188 | "email": "sb@sebastian-bergmann.de", 189 | "role": "lead" 190 | } 191 | ], 192 | "description": "Utility class for timing", 193 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 194 | "keywords": [ 195 | "timer" 196 | ], 197 | "time": "2015-06-21 08:01:12" 198 | }, 199 | { 200 | "name": "phpunit/php-token-stream", 201 | "version": "1.4.8", 202 | "source": { 203 | "type": "git", 204 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 205 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" 206 | }, 207 | "dist": { 208 | "type": "zip", 209 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 210 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 211 | "shasum": "" 212 | }, 213 | "require": { 214 | "ext-tokenizer": "*", 215 | "php": ">=5.3.3" 216 | }, 217 | "require-dev": { 218 | "phpunit/phpunit": "~4.2" 219 | }, 220 | "type": "library", 221 | "extra": { 222 | "branch-alias": { 223 | "dev-master": "1.4-dev" 224 | } 225 | }, 226 | "autoload": { 227 | "classmap": [ 228 | "src/" 229 | ] 230 | }, 231 | "notification-url": "https://packagist.org/downloads/", 232 | "license": [ 233 | "BSD-3-Clause" 234 | ], 235 | "authors": [ 236 | { 237 | "name": "Sebastian Bergmann", 238 | "email": "sebastian@phpunit.de" 239 | } 240 | ], 241 | "description": "Wrapper around PHP's tokenizer extension.", 242 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 243 | "keywords": [ 244 | "tokenizer" 245 | ], 246 | "time": "2015-09-15 10:49:45" 247 | }, 248 | { 249 | "name": "phpunit/phpunit", 250 | "version": "4.0.20", 251 | "source": { 252 | "type": "git", 253 | "url": "https://github.com/sebastianbergmann/phpunit.git", 254 | "reference": "de121ce8708b7ac7f628603d7682d0d57f528345" 255 | }, 256 | "dist": { 257 | "type": "zip", 258 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de121ce8708b7ac7f628603d7682d0d57f528345", 259 | "reference": "de121ce8708b7ac7f628603d7682d0d57f528345", 260 | "shasum": "" 261 | }, 262 | "require": { 263 | "ext-dom": "*", 264 | "ext-json": "*", 265 | "ext-pcre": "*", 266 | "ext-reflection": "*", 267 | "ext-spl": "*", 268 | "php": ">=5.3.3", 269 | "phpunit/php-code-coverage": ">=2.0.0,<2.1.0", 270 | "phpunit/php-file-iterator": "~1.3.1", 271 | "phpunit/php-text-template": "~1.2", 272 | "phpunit/php-timer": "~1.0.2", 273 | "phpunit/phpunit-mock-objects": ">=2.0.0,<2.1.0", 274 | "sebastian/diff": "~1.1", 275 | "sebastian/environment": "~1.0", 276 | "sebastian/exporter": "~1.0.1", 277 | "sebastian/version": "~1.0.3", 278 | "symfony/yaml": "~2.0" 279 | }, 280 | "suggest": { 281 | "phpunit/php-invoker": "~1.1" 282 | }, 283 | "bin": [ 284 | "phpunit" 285 | ], 286 | "type": "library", 287 | "extra": { 288 | "branch-alias": { 289 | "dev-master": "4.0.x-dev" 290 | } 291 | }, 292 | "autoload": { 293 | "classmap": [ 294 | "src/" 295 | ] 296 | }, 297 | "notification-url": "https://packagist.org/downloads/", 298 | "include-path": [ 299 | "", 300 | "../../symfony/yaml/" 301 | ], 302 | "license": [ 303 | "BSD-3-Clause" 304 | ], 305 | "authors": [ 306 | { 307 | "name": "Sebastian Bergmann", 308 | "email": "sebastian@phpunit.de", 309 | "role": "lead" 310 | } 311 | ], 312 | "description": "The PHP Unit Testing framework.", 313 | "homepage": "http://www.phpunit.de/", 314 | "keywords": [ 315 | "phpunit", 316 | "testing", 317 | "xunit" 318 | ], 319 | "time": "2014-05-02 07:19:37" 320 | }, 321 | { 322 | "name": "phpunit/phpunit-mock-objects", 323 | "version": "2.0.10", 324 | "source": { 325 | "type": "git", 326 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 327 | "reference": "e60bb929c50ae4237aaf680a4f6773f4ee17f0a2" 328 | }, 329 | "dist": { 330 | "type": "zip", 331 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/e60bb929c50ae4237aaf680a4f6773f4ee17f0a2", 332 | "reference": "e60bb929c50ae4237aaf680a4f6773f4ee17f0a2", 333 | "shasum": "" 334 | }, 335 | "require": { 336 | "php": ">=5.3.3", 337 | "phpunit/php-text-template": "~1.2" 338 | }, 339 | "require-dev": { 340 | "phpunit/phpunit": ">=4.0.0,<4.1.0" 341 | }, 342 | "suggest": { 343 | "ext-soap": "*" 344 | }, 345 | "type": "library", 346 | "extra": { 347 | "branch-alias": { 348 | "dev-master": "2.0.x-dev" 349 | } 350 | }, 351 | "autoload": { 352 | "classmap": [ 353 | "src/" 354 | ] 355 | }, 356 | "notification-url": "https://packagist.org/downloads/", 357 | "include-path": [ 358 | "" 359 | ], 360 | "license": [ 361 | "BSD-3-Clause" 362 | ], 363 | "authors": [ 364 | { 365 | "name": "Sebastian Bergmann", 366 | "email": "sb@sebastian-bergmann.de", 367 | "role": "lead" 368 | } 369 | ], 370 | "description": "Mock Object library for PHPUnit", 371 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 372 | "keywords": [ 373 | "mock", 374 | "xunit" 375 | ], 376 | "time": "2014-06-12 07:19:48" 377 | }, 378 | { 379 | "name": "sebastian/diff", 380 | "version": "1.3.0", 381 | "source": { 382 | "type": "git", 383 | "url": "https://github.com/sebastianbergmann/diff.git", 384 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" 385 | }, 386 | "dist": { 387 | "type": "zip", 388 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", 389 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", 390 | "shasum": "" 391 | }, 392 | "require": { 393 | "php": ">=5.3.3" 394 | }, 395 | "require-dev": { 396 | "phpunit/phpunit": "~4.2" 397 | }, 398 | "type": "library", 399 | "extra": { 400 | "branch-alias": { 401 | "dev-master": "1.3-dev" 402 | } 403 | }, 404 | "autoload": { 405 | "classmap": [ 406 | "src/" 407 | ] 408 | }, 409 | "notification-url": "https://packagist.org/downloads/", 410 | "license": [ 411 | "BSD-3-Clause" 412 | ], 413 | "authors": [ 414 | { 415 | "name": "Kore Nordmann", 416 | "email": "mail@kore-nordmann.de" 417 | }, 418 | { 419 | "name": "Sebastian Bergmann", 420 | "email": "sebastian@phpunit.de" 421 | } 422 | ], 423 | "description": "Diff implementation", 424 | "homepage": "http://www.github.com/sebastianbergmann/diff", 425 | "keywords": [ 426 | "diff" 427 | ], 428 | "time": "2015-02-22 15:13:53" 429 | }, 430 | { 431 | "name": "sebastian/environment", 432 | "version": "1.3.2", 433 | "source": { 434 | "type": "git", 435 | "url": "https://github.com/sebastianbergmann/environment.git", 436 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" 437 | }, 438 | "dist": { 439 | "type": "zip", 440 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44", 441 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", 442 | "shasum": "" 443 | }, 444 | "require": { 445 | "php": ">=5.3.3" 446 | }, 447 | "require-dev": { 448 | "phpunit/phpunit": "~4.4" 449 | }, 450 | "type": "library", 451 | "extra": { 452 | "branch-alias": { 453 | "dev-master": "1.3.x-dev" 454 | } 455 | }, 456 | "autoload": { 457 | "classmap": [ 458 | "src/" 459 | ] 460 | }, 461 | "notification-url": "https://packagist.org/downloads/", 462 | "license": [ 463 | "BSD-3-Clause" 464 | ], 465 | "authors": [ 466 | { 467 | "name": "Sebastian Bergmann", 468 | "email": "sebastian@phpunit.de" 469 | } 470 | ], 471 | "description": "Provides functionality to handle HHVM/PHP environments", 472 | "homepage": "http://www.github.com/sebastianbergmann/environment", 473 | "keywords": [ 474 | "Xdebug", 475 | "environment", 476 | "hhvm" 477 | ], 478 | "time": "2015-08-03 06:14:51" 479 | }, 480 | { 481 | "name": "sebastian/exporter", 482 | "version": "1.0.2", 483 | "source": { 484 | "type": "git", 485 | "url": "https://github.com/sebastianbergmann/exporter.git", 486 | "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0" 487 | }, 488 | "dist": { 489 | "type": "zip", 490 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", 491 | "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", 492 | "shasum": "" 493 | }, 494 | "require": { 495 | "php": ">=5.3.3" 496 | }, 497 | "require-dev": { 498 | "phpunit/phpunit": "~4.0" 499 | }, 500 | "type": "library", 501 | "extra": { 502 | "branch-alias": { 503 | "dev-master": "1.0.x-dev" 504 | } 505 | }, 506 | "autoload": { 507 | "classmap": [ 508 | "src/" 509 | ] 510 | }, 511 | "notification-url": "https://packagist.org/downloads/", 512 | "license": [ 513 | "BSD-3-Clause" 514 | ], 515 | "authors": [ 516 | { 517 | "name": "Jeff Welch", 518 | "email": "whatthejeff@gmail.com" 519 | }, 520 | { 521 | "name": "Volker Dusch", 522 | "email": "github@wallbash.com" 523 | }, 524 | { 525 | "name": "Bernhard Schussek", 526 | "email": "bschussek@2bepublished.at" 527 | }, 528 | { 529 | "name": "Sebastian Bergmann", 530 | "email": "sebastian@phpunit.de" 531 | }, 532 | { 533 | "name": "Adam Harvey", 534 | "email": "aharvey@php.net" 535 | } 536 | ], 537 | "description": "Provides the functionality to export PHP variables for visualization", 538 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 539 | "keywords": [ 540 | "export", 541 | "exporter" 542 | ], 543 | "time": "2014-09-10 00:51:36" 544 | }, 545 | { 546 | "name": "sebastian/version", 547 | "version": "1.0.6", 548 | "source": { 549 | "type": "git", 550 | "url": "https://github.com/sebastianbergmann/version.git", 551 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 552 | }, 553 | "dist": { 554 | "type": "zip", 555 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 556 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 557 | "shasum": "" 558 | }, 559 | "type": "library", 560 | "autoload": { 561 | "classmap": [ 562 | "src/" 563 | ] 564 | }, 565 | "notification-url": "https://packagist.org/downloads/", 566 | "license": [ 567 | "BSD-3-Clause" 568 | ], 569 | "authors": [ 570 | { 571 | "name": "Sebastian Bergmann", 572 | "email": "sebastian@phpunit.de", 573 | "role": "lead" 574 | } 575 | ], 576 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 577 | "homepage": "https://github.com/sebastianbergmann/version", 578 | "time": "2015-06-21 13:59:46" 579 | }, 580 | { 581 | "name": "symfony/yaml", 582 | "version": "v2.7.4", 583 | "source": { 584 | "type": "git", 585 | "url": "https://github.com/symfony/Yaml.git", 586 | "reference": "2dc7b06c065df96cc686c66da2705e5e18aef661" 587 | }, 588 | "dist": { 589 | "type": "zip", 590 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/2dc7b06c065df96cc686c66da2705e5e18aef661", 591 | "reference": "2dc7b06c065df96cc686c66da2705e5e18aef661", 592 | "shasum": "" 593 | }, 594 | "require": { 595 | "php": ">=5.3.9" 596 | }, 597 | "require-dev": { 598 | "symfony/phpunit-bridge": "~2.7" 599 | }, 600 | "type": "library", 601 | "extra": { 602 | "branch-alias": { 603 | "dev-master": "2.7-dev" 604 | } 605 | }, 606 | "autoload": { 607 | "psr-4": { 608 | "Symfony\\Component\\Yaml\\": "" 609 | } 610 | }, 611 | "notification-url": "https://packagist.org/downloads/", 612 | "license": [ 613 | "MIT" 614 | ], 615 | "authors": [ 616 | { 617 | "name": "Fabien Potencier", 618 | "email": "fabien@symfony.com" 619 | }, 620 | { 621 | "name": "Symfony Community", 622 | "homepage": "https://symfony.com/contributors" 623 | } 624 | ], 625 | "description": "Symfony Yaml Component", 626 | "homepage": "https://symfony.com", 627 | "time": "2015-08-24 07:13:45" 628 | } 629 | ], 630 | "aliases": [], 631 | "minimum-stability": "stable", 632 | "stability-flags": [], 633 | "prefer-stable": false, 634 | "prefer-lowest": false, 635 | "platform": [], 636 | "platform-dev": [] 637 | } 638 | -------------------------------------------------------------------------------- /examples/callFunction.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set the timeout to be pretty short (in case your core is offline) 26 | $particle->setTimeout("5"); 27 | 28 | // Set our access token (set in the phpConfig.config.php file) 29 | $particle->setAccessToken($accessToken); 30 | 31 | // Turn on the D7 LED (requires Tinker to be on your Particle Core) 32 | $particle->debug("Particle Function"); 33 | if($particle->callFunction($deviceID, "digitalwrite", "D7,HIGH") == true) 34 | { 35 | $particle->debug_r($particle->getResult()); 36 | } 37 | else 38 | { 39 | $particle->debug("Error: " . $particle->getError()); 40 | $particle->debug("Error Source" . $particle->getErrorSource()); 41 | } 42 | ?> -------------------------------------------------------------------------------- /examples/claimDevice.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // claim a device on your account 29 | $particle->debug("Claim Device"); 30 | if($particle->claimDevice($deviceID) == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/deleteDevice.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // Remove the device from your account 29 | $particle->debug("Remove Device"); 30 | if($particle->deleteDevice($deviceID) == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/deleteToken.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Delete a specific token 26 | $token = "EXAMPLE_TOKEN"; 27 | $particle->debug("Delete Token " . $token); 28 | $particle->setAuth($username, $password); 29 | if($particle->deleteToken($token) == true) 30 | { 31 | $particle->debug_r($particle->getResult()); 32 | } 33 | else 34 | { 35 | $particle->debug("Error: " . $particle->getError()); 36 | $particle->debug("Error Source" . $particle->getErrorSource()); 37 | } 38 | ?> -------------------------------------------------------------------------------- /examples/deleteWebhook.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | $webhookID = 'WEBHOOK_ID'; 29 | // Delete particle webhook 30 | $particle->debug("Delete Particle Web Hook"); 31 | if($particle->deleteWebhook($webhookID) == true) 32 | { 33 | $particle->debug_r($particle->getResult()); 34 | } 35 | else 36 | { 37 | $particle->debug("Error: " . $particle->getError()); 38 | $particle->debug("Error Source" . $particle->getErrorSource()); 39 | } 40 | ?> -------------------------------------------------------------------------------- /examples/getDeviceInfo.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // Grab a specific device's info 29 | $particle->debug("Particle Device Info"); 30 | if($particle->getAttributes($deviceID) == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/getDeviceInfoInProduct.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // By setting the product slug all applicable operations become product-specific. 29 | $particle->setProductSlug($productSlug); 30 | 31 | // Grab a specific device's info 32 | $particle->debug("Particle Device Info"); 33 | if($particle->getAttributes($deviceID) == true) 34 | { 35 | $particle->debug_r($particle->getResult()); 36 | } 37 | else 38 | { 39 | $particle->debug("Error: " . $particle->getError()); 40 | $particle->debug("Error Source" . $particle->getErrorSource()); 41 | } 42 | ?> -------------------------------------------------------------------------------- /examples/getToken.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Create Particle core token 26 | $particle->debug("generate Token"); 27 | $particle->setAuth($username, $password); 28 | if($particle->createAccessToken() == true) 29 | { 30 | $particle->debug_r($particle->getResult()); 31 | } 32 | else 33 | { 34 | $particle->debug("Error: " . $particle->getError()); 35 | $particle->debug("Error Source" . $particle->getErrorSource()); 36 | } 37 | ?> -------------------------------------------------------------------------------- /examples/getVariable.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // Grab the current uptime of your core (requires a modified version of tinker on your Particle Core) 29 | $particle->debug("Particle Variable"); 30 | if($particle->getVariable($deviceID, "uptime") == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/listDevices.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // List all the devices on your account 29 | $particle->debug("Particle Devices"); 30 | if($particle->listDevices() == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/listDevicesInProduct.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // By setting the product slug all applicable operations become product-specific. 29 | $particle->setProductSlug($productSlug); 30 | 31 | // List all the devices on your account 32 | $particle->debug("Particle Devices"); 33 | if($particle->listDevices() == true) 34 | { 35 | $particle->debug_r($particle->getResult()); 36 | } 37 | else 38 | { 39 | $particle->debug("Error: " . $particle->getError()); 40 | $particle->debug("Error Source" . $particle->getErrorSource()); 41 | } 42 | ?> -------------------------------------------------------------------------------- /examples/listTokens.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // List of Particle core tokens 26 | $particle->debug("Particle Tokens"); 27 | $particle->setAuth($username, $password); 28 | if($particle->listAccessTokens() == true) 29 | { 30 | $particle->debug_r($particle->getResult()); 31 | } 32 | else 33 | { 34 | $particle->debug("Error: " . $particle->getError()); 35 | $particle->debug("Error Source" . $particle->getErrorSource()); 36 | } 37 | ?> -------------------------------------------------------------------------------- /examples/listWebhooks.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // List of Particle core tokens 29 | $particle->debug("Particle Web Hooks"); 30 | if($particle->listWebhooks() == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/newAccessToken.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // List of Particle core tokens 26 | $particle->debug("New Access Token"); 27 | $particle->setAuth($username, $password); 28 | if($particle->newAccessToken() == true) 29 | { 30 | $particle->debug_r($particle->getResult()); 31 | } 32 | else 33 | { 34 | $particle->debug("Error: " . $particle->getError()); 35 | $particle->debug("Error Source" . $particle->getErrorSource()); 36 | } 37 | ?> -------------------------------------------------------------------------------- /examples/newWebhook.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // create particle webhook 29 | $particle->debug("Create Particle Web Hook"); 30 | 31 | $extras = array(); 32 | $extras['mydevices'] = true; 33 | $extras['deviceid'] = $deviceID; 34 | $extras['requestType'] = "POST"; 35 | //$extras['headers'] = array("X-Device-ID"=>"test"); 36 | //$extras["form"] = json_encode(array("form_name"=>"form_value")); // Not implemented server side yet 37 | //$extras['json'] = array("json_key"=>"json_value"); 38 | $extras['query'] = array("p1"=>"v1"); 39 | $extras['auth'] = array("username"=>"test","password"=>"test_password"); 40 | 41 | // headers & auth are mutually exclusive (can't have both at the same time or the call will fail on the cloud side) 42 | // json and query are mutually exclusive 43 | 44 | $fields = array_merge(array('event' => $event, 'url' => $url, 'deviceid' => $deviceID),$extras); 45 | print_r($fields); 46 | 47 | if($particle->newWebhook('test', 'http://google.com/',$extras) == true) 48 | { 49 | $particle->debug_r($particle->getResult()); 50 | } 51 | else 52 | { 53 | $particle->debug("Error: " . $particle->getError()); 54 | $particle->debug("Error Source" . $particle->getErrorSource()); 55 | } 56 | ?> -------------------------------------------------------------------------------- /examples/setDeviceName.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // Rename your Particle Core 29 | $particle->debug("Particle Set Device Name"); 30 | if($particle->renameDevice($deviceID,uniqid('phpParticle_')) == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/signalDevice.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // Signals the particle with a rainbow display using the rgb led 29 | $particle->debug("signal Particle Device"); 30 | if($particle->signalDevice($deviceID,1) == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /examples/tinker.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrisonhjones/phpParticle/26ca3270dac4b326961221c8d2de0e114b407dde/examples/tinker.bin -------------------------------------------------------------------------------- /examples/tinker.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file application.cpp 4 | * @authors Satish Nair, Zachary Crockett and Mohit Bhoite 5 | * @version V1.0.0 6 | * @date 05-November-2013 7 | * @brief Tinker application 8 | ****************************************************************************** 9 | Copyright (c) 2013 Particle Labs, Inc. All rights reserved. 10 | 11 | This program is free software; you can redistribute it and/or 12 | modify it under the terms of the GNU Lesser General Public 13 | License as published by the Free Software Foundation, either 14 | version 3 of the License, or (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public 22 | License along with this program; if not, see . 23 | ****************************************************************************** 24 | */ 25 | 26 | /* Includes ------------------------------------------------------------------*/ 27 | #include "application.h" 28 | 29 | /* Function prototypes -------------------------------------------------------*/ 30 | int tinkerDigitalRead(String pin); 31 | int tinkerDigitalWrite(String command); 32 | int tinkerAnalogRead(String pin); 33 | int tinkerAnalogWrite(String command); 34 | 35 | double upTime = 0; 36 | double firmwareVersion = 1; 37 | 38 | SYSTEM_MODE(AUTOMATIC); 39 | 40 | /* This function is called once at start up ----------------------------------*/ 41 | void setup() 42 | { 43 | //Setup the Tinker application here 44 | 45 | //Register all the Tinker functions 46 | Particle.function("digitalread", tinkerDigitalRead); 47 | Particle.function("digitalwrite", tinkerDigitalWrite); 48 | 49 | Particle.function("analogread", tinkerAnalogRead); 50 | Particle.function("analogwrite", tinkerAnalogWrite); 51 | 52 | // Register a Particle Variable as well 53 | Particle.variable("uptime", &upTime, DOUBLE); 54 | 55 | // Register a Particle Variable for the firmware version 56 | Particle.variable("firmwareVersion", &firmwareVersion, DOUBLE); 57 | 58 | } 59 | 60 | /* This function loops forever --------------------------------------------*/ 61 | void loop() 62 | { 63 | //This will run in a loop 64 | upTime = millis(); 65 | } 66 | 67 | /******************************************************************************* 68 | * Function Name : tinkerDigitalRead 69 | * Description : Reads the digital value of a given pin 70 | * Input : Pin 71 | * Output : None. 72 | * Return : Value of the pin (0 or 1) in INT type 73 | Returns a negative number on failure 74 | *******************************************************************************/ 75 | int tinkerDigitalRead(String pin) 76 | { 77 | //convert ascii to integer 78 | int pinNumber = pin.charAt(1) - '0'; 79 | //Sanity check to see if the pin numbers are within limits 80 | if (pinNumber< 0 || pinNumber >7) return -1; 81 | 82 | if(pin.startsWith("D")) 83 | { 84 | pinMode(pinNumber, INPUT_PULLDOWN); 85 | return digitalRead(pinNumber); 86 | } 87 | else if (pin.startsWith("A")) 88 | { 89 | pinMode(pinNumber+10, INPUT_PULLDOWN); 90 | return digitalRead(pinNumber+10); 91 | } 92 | return -2; 93 | } 94 | 95 | /******************************************************************************* 96 | * Function Name : tinkerDigitalWrite 97 | * Description : Sets the specified pin HIGH or LOW 98 | * Input : Pin and value 99 | * Output : None. 100 | * Return : 1 on success and a negative number on failure 101 | *******************************************************************************/ 102 | int tinkerDigitalWrite(String command) 103 | { 104 | bool value = 0; 105 | //convert ascii to integer 106 | int pinNumber = command.charAt(1) - '0'; 107 | //Sanity check to see if the pin numbers are within limits 108 | if (pinNumber< 0 || pinNumber >7) return -1; 109 | 110 | if(command.substring(3,7) == "HIGH") value = 1; 111 | else if(command.substring(3,6) == "LOW") value = 0; 112 | else return -2; 113 | 114 | if(command.startsWith("D")) 115 | { 116 | pinMode(pinNumber, OUTPUT); 117 | digitalWrite(pinNumber, value); 118 | return 1; 119 | } 120 | else if(command.startsWith("A")) 121 | { 122 | pinMode(pinNumber+10, OUTPUT); 123 | digitalWrite(pinNumber+10, value); 124 | return 1; 125 | } 126 | else return -3; 127 | } 128 | 129 | /******************************************************************************* 130 | * Function Name : tinkerAnalogRead 131 | * Description : Reads the analog value of a pin 132 | * Input : Pin 133 | * Output : None. 134 | * Return : Returns the analog value in INT type (0 to 4095) 135 | Returns a negative number on failure 136 | *******************************************************************************/ 137 | int tinkerAnalogRead(String pin) 138 | { 139 | //convert ascii to integer 140 | int pinNumber = pin.charAt(1) - '0'; 141 | //Sanity check to see if the pin numbers are within limits 142 | if (pinNumber< 0 || pinNumber >7) return -1; 143 | 144 | if(pin.startsWith("D")) 145 | { 146 | pinMode(pinNumber, INPUT); 147 | return analogRead(pinNumber); 148 | } 149 | else if (pin.startsWith("A")) 150 | { 151 | pinMode(pinNumber+10, INPUT); 152 | return analogRead(pinNumber+10); 153 | } 154 | return -2; 155 | } 156 | 157 | /******************************************************************************* 158 | * Function Name : tinkerAnalogWrite 159 | * Description : Writes an analog value (PWM) to the specified pin 160 | * Input : Pin and Value (0 to 255) 161 | * Output : None. 162 | * Return : 1 on success and a negative number on failure 163 | *******************************************************************************/ 164 | int tinkerAnalogWrite(String command) 165 | { 166 | //convert ascii to integer 167 | int pinNumber = command.charAt(1) - '0'; 168 | //Sanity check to see if the pin numbers are within limits 169 | if (pinNumber< 0 || pinNumber >7) return -1; 170 | 171 | String value = command.substring(3); 172 | 173 | if(command.startsWith("D")) 174 | { 175 | pinMode(pinNumber, OUTPUT); 176 | analogWrite(pinNumber, value.toInt()); 177 | return 1; 178 | } 179 | else if(command.startsWith("A")) 180 | { 181 | pinMode(pinNumber+10, OUTPUT); 182 | analogWrite(pinNumber+10, value.toInt()); 183 | return 1; 184 | } 185 | else return -2; 186 | } -------------------------------------------------------------------------------- /examples/uploadFirmware.php: -------------------------------------------------------------------------------- 1 | debug(...) by you ignore this line and display always 21 | $particle->setDebug(true); 22 | // Set the debug calls to display pretty HTML format. Other option is "TEXT". Note, calls made to $particle->debug(...) display as set here 23 | $particle->setDebugType("HTML"); 24 | 25 | // Set our access token (set in the phpConfig.config.php file) 26 | $particle->setAccessToken($accessToken); 27 | 28 | // Upload firmware to your Particle Core 29 | $particle->debug("Particle Firmware Upload"); 30 | if($particle->uploadFirmware($deviceID,"tinker.bin","tinker.bin",false) == true) 31 | { 32 | $particle->debug_r($particle->getResult()); 33 | } 34 | else 35 | { 36 | $particle->debug("Error: " . $particle->getError()); 37 | $particle->debug("Error Source" . $particle->getErrorSource()); 38 | } 39 | ?> -------------------------------------------------------------------------------- /phpParticle.class.php: -------------------------------------------------------------------------------- 1 | _endpoint = $endpoint; 38 | } 39 | 40 | /** 41 | * Sets the timeout used for calls against the api. 42 | * 43 | * @param int $timeout The amount of time, in seconds, for a call to wait for data before returning with a TIMEOUT error 44 | * 45 | * @return void 46 | * 47 | */ 48 | public function setTimeout($timeout) 49 | { 50 | if(is_numeric($timeout)) 51 | { 52 | $this->_curlTimeout = intval($timeout); 53 | return true; 54 | } 55 | else 56 | { 57 | $errorText = "Non numeric timeout"; 58 | $this->_setError($errorText, __FUNCTION__); 59 | return false; 60 | } 61 | } 62 | 63 | /** 64 | * Sets the authentication details for authenticating with the API 65 | * 66 | * @param string $email The email to authenticate with 67 | * @param string $password The password to authenticate with 68 | * 69 | * @return void 70 | * 71 | */ 72 | public function setAuth($email, $password) 73 | { 74 | $this->_email = $email; 75 | $this->_password = $password; 76 | } 77 | 78 | /** 79 | * Clears all the authentication info (email and password). Internally set to false. Subsequent calls which require a email/password will fail 80 | * 81 | * @return void 82 | * 83 | */ 84 | public function clearAuth() 85 | { 86 | $this->setAuth(false,false); 87 | } 88 | 89 | /** 90 | * Sets the access token for authenticating with the API 91 | * 92 | * @param string $accessToken The access token to authenticate with 93 | * 94 | * @return void 95 | * 96 | */ 97 | public function setAccessToken($accessToken) 98 | { 99 | $this->_accessToken = $accessToken; 100 | } 101 | 102 | /** 103 | * Sets the product slug for authenticating with the API 104 | * 105 | * @param string $accessToken The access token to authenticate with 106 | * 107 | * @return void 108 | * 109 | */ 110 | public function setProductSlug($productSlug) 111 | { 112 | $this->_productSlug = $productSlug; 113 | } 114 | 115 | /** 116 | * Clears the accesss token info. Internally set to false. Subsequent calls which require an access token will fail 117 | * 118 | * @return void 119 | * 120 | */ 121 | public function clearAccessToken() 122 | { 123 | $this->setAccessToken(false); 124 | } 125 | 126 | /** 127 | * Sets the debug type. Use "HTML" for errors automatically formatted for embedding into a webpage and "TEXT" for unformatted raw errors 128 | * 129 | * @param string $debugType The debug type (either "HTML" or "TEXT") 130 | * 131 | * @return void 132 | * 133 | */ 134 | public function setDebugType($debugType = "HTML") 135 | { 136 | if(($debugType == "HTML") or ($debugType == "TEXT")) 137 | { 138 | $this->_debugType = $debugType; 139 | return true; 140 | } 141 | else 142 | { 143 | $this->_setError("Bad debut type (" . $debugType . ")", "setDebugType"); 144 | return false; 145 | } 146 | } 147 | 148 | /** 149 | * Turn internal debugging on or off. Note, external calls made to debug ($obj->debug(...)) will always display regardless of this setting 150 | * 151 | * @param boolean $debug true turns on internal debugging & false turns off internal debugging 152 | * 153 | * @return void 154 | * 155 | */ 156 | public function setDebug($debug = false) 157 | { 158 | if($debug) 159 | { 160 | $this->_debug = true; 161 | return true; 162 | } 163 | else 164 | { 165 | $this->_debug = false; 166 | return true; 167 | } 168 | } 169 | 170 | /** 171 | * Turn on or off SSL verification (it's a CURL thing). For testing, before you get the certificates setup, you might need to disable SSL verificatioon. Note this is a security concern 172 | * 173 | * @param boolean $disableSSL true allows you to communicate with api endpoints with invalid security certificates & false enforces SSL verification 174 | * 175 | * @return void 176 | * 177 | */ 178 | public function setDisableSSL($disableSSL = false) 179 | { 180 | if($disableSSL) 181 | { 182 | $this->_disableSSL = true; 183 | return true; 184 | } 185 | else 186 | { 187 | $this->_disableSSL = false; 188 | return true; 189 | } 190 | } 191 | 192 | /** 193 | * Private Function. Sets the internal _error & _errorSource variables. Allow for tracking which function resulted in an error and what that error was 194 | * 195 | * @param string $errorText The value to set _error to 196 | * @param string $errorSource The value to set _errorSource to 197 | * 198 | * @return void 199 | * 200 | */ 201 | private function _setError($errorText, $errorSource) 202 | { 203 | $this->_error = $errorText; 204 | $this->_errorSource = $errorSource; 205 | } 206 | 207 | /** 208 | * Private Function. Sets the internal _errorSource. Allow for tracking which function resulted in an error 209 | * 210 | * @param string $errorSource The value to set _errorSource to 211 | * 212 | * @return void 213 | * 214 | */ 215 | private function _setErrorSource($errorSource) 216 | { 217 | $this->_errorSource = $errorSource; 218 | } 219 | 220 | /** 221 | * Private Function. Outputs the desired debug text formatted if required 222 | * 223 | * @param string $debugText The debug string to output 224 | * @param string $override If set to true overrides the internal debug on/off state and always outputs the debugText. If set to false it follows the internal debug on/off state 225 | * 226 | * @return void 227 | * 228 | */ 229 | private function _debug($debugText, $override = false) 230 | { 231 | if(($this->_debug == true) || ($override == true)) 232 | { 233 | if($this->_debugType == "HTML") 234 | { 235 | echo $debugText . "
\n"; 236 | return true; 237 | } 238 | else if($this->_debugType == "TEXT") 239 | { 240 | echo $debugText . "\n"; 241 | return true; 242 | } 243 | else 244 | { 245 | $this->_setError("Bad debut type (" . $this->_debugType . ")", "_debug"); 246 | return false; 247 | } 248 | } 249 | } 250 | 251 | /** 252 | * Private Function. Outputs the desired debug array formatted if required 253 | * 254 | * @param mixed[] $debugArray The debug array to output 255 | * @param string $override If set to true overrides the internal debug on/off state and always outputs the debugArray. If set to false it follows the internal debug on/off state 256 | * 257 | * @return void 258 | * 259 | */ 260 | private function _debug_r($debugArray, $override = false) 261 | { 262 | if(($this->_debug == true) || ($override == true)) 263 | { 264 | if($this->_debugType == "HTML") 265 | { 266 | $this->debug("
");
267 |                 print_r($debugArray);
268 |                 $this->debug("
"); 269 | return true; 270 | } 271 | else if($this->_debugType == "TEXT") 272 | { 273 | print_r($debugArray); 274 | $this->debug(); 275 | return true; 276 | } 277 | else 278 | { 279 | $this->_setError("Bad debut type (" . $this->_debugType . ")", "_debug"); 280 | return false; 281 | } 282 | } 283 | } 284 | 285 | /** 286 | * Outputs the desired debug text formatted if required 287 | * 288 | * @param string $debugText The debug string to output 289 | * 290 | * @return void 291 | * 292 | */ 293 | public function debug($debugText) 294 | { 295 | return $this->_debug($debugText, $override = true); 296 | } 297 | 298 | /** 299 | * Outputs the desired debug array formatted if required 300 | * 301 | * @param string $debugArray The debug array to output 302 | * 303 | * @return void 304 | * 305 | */ 306 | public function debug_r($debugArray) 307 | { 308 | return $this->_debug_r($debugArray, $override = true); 309 | } 310 | 311 | /** 312 | * Runs a particle function on the device. Requires the accessToken to be set 313 | * 314 | * @param string $deviceID The device ID of the device to call the function on 315 | * @param string $deviceFunction The name function to call 316 | * @param string $params The parameters to send to the function (the 'args') 317 | * 318 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 319 | */ 320 | public function callFunction($deviceID, $deviceFunction, $params) 321 | { 322 | if(empty($this->_productSlug)) { 323 | $url = $this->_endpoint . 'v1/devices/' . $deviceID . '/' . $deviceFunction; 324 | } else { 325 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/' . $deviceID . '/' . $deviceFunction; 326 | } 327 | return $this->_curlRequest($url, array('args'=>$params), 'post'); 328 | } 329 | 330 | /** 331 | * Gets the value of a particle variable. Requires the accessToken to be set 332 | * 333 | * @param string $deviceID The device ID of the device to call the function on 334 | * @param string $variableName The name of the variable to retrieve 335 | * 336 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 337 | */ 338 | public function getVariable($deviceID, $variableName) 339 | { 340 | if(empty($this->_productSlug)) { 341 | $url = $this->_endpoint . 'v1/devices/' . $deviceID . '/' . $variableName; 342 | } else { 343 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/' . $deviceID . '/' . $variableName; 344 | } 345 | return $this->_curlRequest($url, array(), 'get'); 346 | } 347 | 348 | /** 349 | * Lists all your cores assigned to your cloud account. Requires the accessToken to be set 350 | * 351 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 352 | */ 353 | public function listDevices() 354 | { 355 | if(empty($this->_productSlug)) { 356 | $url = $this->_endpoint . 'v1/devices/'; 357 | return $this->_curlRequest($url, array(), 'get'); 358 | } else { 359 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/'; 360 | return $this->_curlRequest($url, array(), 'get', 'none', 'devices'); 361 | } 362 | } 363 | /** 364 | * Gets your details from your core e.g. function and variables. Requires the accessToken to be set 365 | * 366 | * @param string $deviceID The device ID of the device 367 | * 368 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 369 | */ 370 | public function getAttributes($deviceID) 371 | { 372 | if(empty($this->_productSlug)) { 373 | $url = $this->_endpoint . 'v1/devices/' . $deviceID; 374 | } else { 375 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/' . $deviceID; 376 | } 377 | return $this->_curlRequest($url, array(), 'get'); 378 | } 379 | 380 | /** 381 | * Set the name/renames your core. Requires the accessToken to be set 382 | * 383 | * @param string $deviceID The device ID of the device to rename 384 | * @param string $name The new name of the device 385 | * 386 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 387 | */ 388 | public function renameCore($deviceID,$name) 389 | { 390 | if(empty($this->_productSlug)) { 391 | $url = $this->_endpoint . 'v1/devices/' . $deviceID; 392 | } else { 393 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/' . $deviceID; 394 | } 395 | return $this->_curlRequest($url, array("name" => $name), 'put'); 396 | } 397 | 398 | /** 399 | * Attempts to add a device to your cloud account. Requires the accessToken to be set. Note, you may want to follow this up with a call to "setName" as new Core's names are blank. Interestingly, if claiming an order core their name is retained across the unclaim/claim process 400 | * 401 | * @param string $deviceID The device ID of the device to claim. 402 | * @param boolean $requestTransfer If true requests that the device be transfered to your account (use if the device is already claimed). If false will try to claim but not automatically send a transfer request 403 | * 404 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 405 | */ 406 | 407 | public function claimDevice($deviceID, $requestTransfer = false) 408 | { 409 | if(empty($this->_productSlug)) { 410 | $url = $this->_endpoint . 'v1/devices'; 411 | } else { 412 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices'; 413 | } 414 | if($requestTransfer) 415 | $result = $this->_curlRequest($url, array('id' => $deviceID, 'request_transfer' => 'true'), 'post'); 416 | else 417 | $result = $this->_curlRequest($url, array('id' => $deviceID, 'request_transfer' => 'false'), 'post'); 418 | 419 | return $result; 420 | } 421 | 422 | /** 423 | * Removes the core from your cloud account. Requires the accessToken to be set 424 | * 425 | * @param string $deviceID The device ID of the device to remove from your account. 426 | * 427 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 428 | */ 429 | public function removeDevice($deviceID) 430 | { 431 | if(empty($this->_productSlug)) { 432 | $url = $this->_endpoint . 'v1/devices/' . $deviceID; 433 | } else { 434 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/' . $deviceID; 435 | } 436 | return $this->_curlRequest($url, array(), 'delete'); 437 | } 438 | 439 | /** 440 | * Uploads a sketch to the core. Requires the accessToken to be set 441 | * 442 | * @param string $deviceID The device ID of the device to upload the code to 443 | * @param string $filename The filename of the firmware file to upload to the device. Ex: tinker.cpp. Not yet implemented 444 | * @param string $filepath The path to the firmware file to upload (including the name). Ex: path/to/tinker.cpp 445 | * @param boolean $isBinary Set to true if uploading a .bin file or false otherwise. 446 | * 447 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 448 | */ 449 | public function uploadFirmware($deviceID,$filename,$filepath,$isBinary=false) 450 | { 451 | if(empty($this->_productSlug)) { 452 | $url = $this->_endpoint . 'v1/devices/' . $deviceID; 453 | } else { 454 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/' . $deviceID; 455 | } 456 | // Create a CURLFile object 457 | $cfile = new CURLFile($filepath,'application/octet-stream',$filename); 458 | $params = array('file' => $cfile); 459 | if($isBinary == true) 460 | $params['file_type'] = "binary"; 461 | return $this->_curlRequest($url, $params, 'put-file'); 462 | } 463 | 464 | /** 465 | * Gets a list of your tokens from the particle cloud. Requires the email/password auth to be set 466 | * 467 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 468 | */ 469 | public function listAccessTokens() 470 | { 471 | $url = $this->_endpoint .'v1/access_tokens'; 472 | $result = $this->_curlRequest($url, array(), 'get', 'basic'); 473 | 474 | return $result; 475 | } 476 | 477 | /** 478 | * Creates a new token on the particle cloud. Requires the email/password auth to be set 479 | * 480 | * @param int $expires_in When the token should expire (in seconds). Set to false to ignore and use the default. Set to 0 for a token that never expires 481 | * @param string $expires_at When the token should expire (at a date/time). Set to false to ignore and use the default. Set to 'null' for a token that never expires. Otherwise this should be a ISO8601 style date string 482 | * @param string $clientID The clientID. If you don't have one of these (only used in OAuth applications) set to false 483 | * @param string $clientSecret The clientSecret. If you don't have one of these (only used in OAuth applications) set to false 484 | * 485 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 486 | */ 487 | 488 | public function newAccessToken($expires_in = false, $expires_at = false, $clientID = false, $clientSecret = false) 489 | { 490 | $fields = array('grant_type' => 'password', 'username' => $this->_email, 'password' => $this->_password); 491 | 492 | if($expires_in !== false) 493 | $fields['expires_in'] = intval($expires_in); 494 | 495 | if($expires_at !== false) 496 | $fields['expires_at'] = $expires_at; 497 | 498 | if($clientID) 499 | { 500 | $fields['client_id'] = $clientID; 501 | $fields['client_secret'] = $clientSecret; 502 | } 503 | 504 | $url = $this->_endpoint .'oauth/token'; 505 | $result = $this->_curlRequest($url, $fields, 'post', 'basic-dummy'); 506 | 507 | return $result; 508 | } 509 | 510 | /** 511 | * Removes the token from the particle cloud. Requires the email/password auth to be set 512 | * 513 | * @param string $token The access token to remove 514 | * 515 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 516 | */ 517 | public function deleteAccessToken($token) 518 | { 519 | $url = $this->_endpoint .'v1/access_tokens/'.$token; 520 | $result = $this->_curlRequest($url, array(), 'delete', 'basic'); 521 | 522 | return $result; 523 | } 524 | 525 | /** 526 | * Gets a list of webhooks from the particle cloud. Requires the accessToken to be set 527 | * 528 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 529 | */ 530 | public function listWebhooks() 531 | { 532 | $fields = array(); 533 | $url = $this->_endpoint .'v1/webhooks'; 534 | $result = $this->_curlRequest($url, $fields, 'get'); 535 | 536 | return $result; 537 | } 538 | 539 | /** 540 | * Creates a new webhook on the particle cloud. Requires the accessToken to be set 541 | * @param string $event The event name used to trigger the webhook 542 | * @param string $webhookUrl The url to query once the event has occured 543 | * @param string $extras See http://docs.particle.io/webhooks/#webhook-options 544 | * 545 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 546 | */ 547 | public function newWebhook($event, $webhookUrl, $extras = array()) 548 | { 549 | $url = $this->_endpoint .'v1/webhooks/'; 550 | 551 | $fields = array_merge(array('event' => $event, 'url' => $webhookUrl),$extras); 552 | 553 | $result = $this->_curlRequest($url, $fields , 'post'); 554 | 555 | return $result; 556 | } 557 | 558 | /** 559 | * Delete webhooks from the particle cloud. Requires the accessToken to be set 560 | * 561 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 562 | */ 563 | public function deleteWebhook($webhookID) 564 | { 565 | $fields = array(); 566 | $url = $this->_endpoint ."v1/webhooks/{$webhookID}/"; 567 | $result = $this->_curlRequest($url, $fields, 'delete'); 568 | 569 | return $result; 570 | } 571 | 572 | /** 573 | * Sets the particle core signal mode state. Requires the accessToken to be set 574 | * 575 | * @param string $deviceID The device ID of the device to send the signal mode state change command to. 576 | * @param int $signalState The signal state: 0 returns the RGB led back to normmal & 1 makes it flash a rainbow of color 577 | * 578 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 579 | */ 580 | public function signalDevice($deviceID, $signalState = 0) 581 | { 582 | if(empty($this->_productSlug)) { 583 | $url = $this->_endpoint . 'v1/devices/' . $deviceID; 584 | } else { 585 | $url = $this->_endpoint . 'v1/products/' . $this->_productSlug . '/devices/' . $deviceID; 586 | } 587 | $fields = array('signal' => $signalState); 588 | return $this->_curlRequest($url, $fields, 'put'); 589 | } 590 | 591 | /** 592 | * Returns the latest error 593 | * 594 | * @return string The latest error 595 | */ 596 | public function getError() 597 | { 598 | return $this->_error; 599 | } 600 | 601 | /** 602 | * Returns the latest error's source (which function cause the error) 603 | * 604 | * @return string The latest error's source 605 | */ 606 | public function getErrorSource() 607 | { 608 | return $this->_errorSource; 609 | } 610 | 611 | /** 612 | * Returns the latest result 613 | * 614 | * @return string The latest result from calling a cloud function 615 | */ 616 | public function getResult() 617 | { 618 | return $this->_result; 619 | } 620 | 621 | /** 622 | * Private Function. Performs a CURL Request with the given parameters 623 | * 624 | * @param string url The url to call 625 | * @param mixed[] params An array of parameters to pass to the url 626 | * @param string type The type of request ("GET", "POST", "PUT", etc) 627 | * @param string authType The type of authorization to use ('none' uses the access token, 'basic' uses basic auth with the email/password auth details, and 'basic-dummy' uses dummy basic auth details) 628 | * 629 | * @return boolean true on success, false on failure 630 | */ 631 | private function _curlRequest($url, $params = null, $type = 'post', $authType = 'none', $key = false) 632 | { 633 | 634 | $fields_string = null; 635 | 636 | if($authType == 'none') 637 | if ($this->_accessToken) 638 | { 639 | $params['access_token'] = $this->_accessToken; 640 | } 641 | else 642 | { 643 | $errorText = "No access token set"; 644 | list(, $caller) = debug_backtrace(false); 645 | $this->_setError($errorText, $caller['function']); 646 | return false; 647 | } 648 | 649 | 650 | // is cURL installed yet? 651 | if (!function_exists('curl_init')) 652 | { 653 | die("CURL is not installed/available"); 654 | } 655 | 656 | // OK cool - then let's create a new cURL resource handle 657 | $ch = curl_init(); 658 | //set the number of POST vars, POST data 659 | if($type == 'get') 660 | { 661 | $url .= ("?" . http_build_query($params)); 662 | } 663 | else if ($type == 'post') 664 | { 665 | curl_setopt($ch,CURLOPT_POST,count($params)); 666 | curl_setopt($ch,CURLOPT_POSTFIELDS,http_build_query($params)); 667 | } 668 | else if($type == "put") 669 | { 670 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); 671 | curl_setopt($ch,CURLOPT_POSTFIELDS,http_build_query($params)); 672 | } 673 | else if($type == "put-file") 674 | { 675 | curl_setopt($ch, CURLOPT_POST, true); 676 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); 677 | unset($params['access_token']); 678 | curl_setopt($ch,CURLOPT_POSTFIELDS,$params); 679 | $url .= "?access_token=" . $this->_accessToken; 680 | } 681 | else if ($type == 'delete') 682 | { 683 | $url .= ("?" . http_build_query($params)); 684 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); 685 | } 686 | else 687 | { 688 | $errorText = "Unsupported method type (" . $type . ")"; 689 | $this->_setError($errorText, __FUNCTION__); 690 | return false; 691 | } 692 | 693 | $this->_debug("Opening a {$type} connection to {$url}"); 694 | curl_setopt($ch, CURLOPT_URL, $url); 695 | 696 | if($this->_disableSSL) 697 | { 698 | // stop the verification of certificate 699 | $this->_debug("[WARN] Disabling SSL Verification for CURL"); 700 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 701 | } 702 | 703 | // Set a referer 704 | // curl_setopt($ch, CURLOPT_REFERER, "http://www.example.com/curl.htm"); 705 | 706 | // User agent 707 | // curl_setopt($ch, CURLOPT_USERAGENT, "MozillaXYZ/1.0"); 708 | 709 | // Include header in result? (0 = yes, 1 = no) 710 | curl_setopt($ch, CURLOPT_HEADER, 0); 711 | 712 | // Should cURL return or print out the data? (true = return, false = print) 713 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 714 | 715 | // Timeout in seconds 716 | curl_setopt($ch, CURLOPT_TIMEOUT, $this->_curlTimeout); 717 | 718 | $this->_debug("Auth Type: " . $authType); 719 | // basic auth 720 | if ($authType == 'basic') { 721 | if(($this->_email) && ($this->_password)) 722 | { 723 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 724 | curl_setopt($ch, CURLOPT_USERPWD, $this->_email . ":" . $this->_password); 725 | } 726 | else 727 | { 728 | list(, $caller) = debug_backtrace(false); 729 | $errorText = "No auth credentials (email/password) set"; 730 | $this->_setError($errorText, $caller['function']); 731 | return false; 732 | } 733 | } 734 | if ($authType == 'basic-dummy') { 735 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 736 | curl_setopt($ch, CURLOPT_USERPWD, "particle:particle"); 737 | } 738 | 739 | // Download the given URL, and return output 740 | $this->_debug("Executing Curl Operation"); 741 | $this->_debug("Url:"); 742 | $this->_debug_r($url); 743 | $this->_debug("Params:"); 744 | $this->_debug_r($params); 745 | $output = curl_exec($ch); 746 | 747 | $this->_debug("Curl Result: '" . $output . "'"); 748 | 749 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 750 | $this->_debug("Curl Response Code: '" . $httpCode."'"); 751 | // Close the cURL resource, and free system resources 752 | 753 | $curlError = curl_errno($ch); 754 | curl_close($ch); 755 | if($curlError != CURLE_OK) 756 | { 757 | $this->_debug("CURL Request - There was a CURL error"); 758 | list(, $caller) = debug_backtrace(false); 759 | //var_dump($caller['function']); 760 | $errorText = $this->_curlErrorCode($curlError); 761 | $this->_setError($errorText, $caller['function']); 762 | return false; 763 | } 764 | else 765 | { 766 | $retVal = json_decode($output,true); 767 | 768 | if(json_last_error() == 0) 769 | { 770 | if(isset($retVal['error']) && $retVal['error']) 771 | { 772 | $this->_debug("CURL Request - API response contained 'error' field"); 773 | $errorText = $retVal['error']; 774 | $this->_setError($errorText, __FUNCTION__); 775 | return false; 776 | } 777 | else 778 | { 779 | $this->_debug("CURL Request - Returning True"); 780 | if($key != false) { 781 | $this->_debug("CURL Request - Extracting from key '" . $key . "'"); 782 | $this->_result = $retVal[$key]; 783 | } else { 784 | $this->_result = $retVal; 785 | } 786 | 787 | return true; 788 | } 789 | } 790 | else 791 | { 792 | $this->_debug("CURL Request - Unable to parse JSON"); 793 | $errorText = "Unable to parse JSON. Json error = " . json_last_error() . ". See http://php.net/manual/en/function.json-last-error.php for more information. Raw response from Particle Cloud = '" . $result . "'"; 794 | $this->_setError($errorText, __FUNCTION__); 795 | return false; 796 | } 797 | } 798 | } 799 | 800 | /** 801 | * Private Function. Returns a human readable string for a given CURL Error Code 802 | * 803 | * @param int curlCode The CURL error code 804 | * 805 | * @return string A human-readable string version of the curlCode 806 | */ 807 | private function _curlErrorCode($curlCode) 808 | { 809 | switch ($curlCode) 810 | { 811 | case 26: 812 | return "Curl Error. There was a problem reading a local file or an error returned by the read callback."; 813 | case 30: 814 | return "Curl Error. Operation timeout. The specified time-out period was reached according to the conditions."; 815 | default: 816 | return "Curl Error. Error number = {$curlCode}. See http://curl.haxx.se/libcurl/c/libcurl-errors.html for more information"; 817 | } 818 | } 819 | } -------------------------------------------------------------------------------- /phpParticle.config.sample.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /phpParticle.firmware.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file application.cpp 4 | * @authors Satish Nair, Zachary Crockett and Mohit Bhoite 5 | * @version V1.0.0 6 | * @date 05-November-2013 7 | * @brief Tinker application 8 | ****************************************************************************** 9 | Copyright (c) 2013 Particle Labs, Inc. All rights reserved. 10 | 11 | This program is free software; you can redistribute it and/or 12 | modify it under the terms of the GNU Lesser General Public 13 | License as published by the Free Software Foundation, either 14 | version 3 of the License, or (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | Lesser General Public License for more details. 20 | 21 | You should have received a copy of the GNU Lesser General Public 22 | License along with this program; if not, see . 23 | ****************************************************************************** 24 | */ 25 | 26 | /* Includes ------------------------------------------------------------------*/ 27 | #include "application.h" 28 | 29 | /* Function prototypes -------------------------------------------------------*/ 30 | int tinkerDigitalRead(String pin); 31 | int tinkerDigitalWrite(String command); 32 | int tinkerAnalogRead(String pin); 33 | int tinkerAnalogWrite(String command); 34 | 35 | double upTime = 0; 36 | 37 | SYSTEM_MODE(AUTOMATIC); 38 | 39 | /* This function is called once at start up ----------------------------------*/ 40 | void setup() 41 | { 42 | //Setup the Tinker application here 43 | 44 | //Register all the Tinker functions 45 | Particle.function("digitalread", tinkerDigitalRead); 46 | Particle.function("digitalwrite", tinkerDigitalWrite); 47 | 48 | Particle.function("analogread", tinkerAnalogRead); 49 | Particle.function("analogwrite", tinkerAnalogWrite); 50 | 51 | // Register a Particle Variable as well 52 | Particle.variable("uptime", &upTime, DOUBLE); 53 | 54 | } 55 | 56 | /* This function loops forever --------------------------------------------*/ 57 | void loop() 58 | { 59 | //This will run in a loop 60 | upTime = millis(); 61 | } 62 | 63 | /******************************************************************************* 64 | * Function Name : tinkerDigitalRead 65 | * Description : Reads the digital value of a given pin 66 | * Input : Pin 67 | * Output : None. 68 | * Return : Value of the pin (0 or 1) in INT type 69 | Returns a negative number on failure 70 | *******************************************************************************/ 71 | int tinkerDigitalRead(String pin) 72 | { 73 | //convert ascii to integer 74 | int pinNumber = pin.charAt(1) - '0'; 75 | //Sanity check to see if the pin numbers are within limits 76 | if (pinNumber< 0 || pinNumber >7) return -1; 77 | 78 | if(pin.startsWith("D")) 79 | { 80 | pinMode(pinNumber, INPUT_PULLDOWN); 81 | return digitalRead(pinNumber); 82 | } 83 | else if (pin.startsWith("A")) 84 | { 85 | pinMode(pinNumber+10, INPUT_PULLDOWN); 86 | return digitalRead(pinNumber+10); 87 | } 88 | return -2; 89 | } 90 | 91 | /******************************************************************************* 92 | * Function Name : tinkerDigitalWrite 93 | * Description : Sets the specified pin HIGH or LOW 94 | * Input : Pin and value 95 | * Output : None. 96 | * Return : 1 on success and a negative number on failure 97 | *******************************************************************************/ 98 | int tinkerDigitalWrite(String command) 99 | { 100 | bool value = 0; 101 | //convert ascii to integer 102 | int pinNumber = command.charAt(1) - '0'; 103 | //Sanity check to see if the pin numbers are within limits 104 | if (pinNumber< 0 || pinNumber >7) return -1; 105 | 106 | if(command.substring(3,7) == "HIGH") value = 1; 107 | else if(command.substring(3,6) == "LOW") value = 0; 108 | else return -2; 109 | 110 | if(command.startsWith("D")) 111 | { 112 | pinMode(pinNumber, OUTPUT); 113 | digitalWrite(pinNumber, value); 114 | return 1; 115 | } 116 | else if(command.startsWith("A")) 117 | { 118 | pinMode(pinNumber+10, OUTPUT); 119 | digitalWrite(pinNumber+10, value); 120 | return 1; 121 | } 122 | else return -3; 123 | } 124 | 125 | /******************************************************************************* 126 | * Function Name : tinkerAnalogRead 127 | * Description : Reads the analog value of a pin 128 | * Input : Pin 129 | * Output : None. 130 | * Return : Returns the analog value in INT type (0 to 4095) 131 | Returns a negative number on failure 132 | *******************************************************************************/ 133 | int tinkerAnalogRead(String pin) 134 | { 135 | //convert ascii to integer 136 | int pinNumber = pin.charAt(1) - '0'; 137 | //Sanity check to see if the pin numbers are within limits 138 | if (pinNumber< 0 || pinNumber >7) return -1; 139 | 140 | if(pin.startsWith("D")) 141 | { 142 | pinMode(pinNumber, INPUT); 143 | return analogRead(pinNumber); 144 | } 145 | else if (pin.startsWith("A")) 146 | { 147 | pinMode(pinNumber+10, INPUT); 148 | return analogRead(pinNumber+10); 149 | } 150 | return -2; 151 | } 152 | 153 | /******************************************************************************* 154 | * Function Name : tinkerAnalogWrite 155 | * Description : Writes an analog value (PWM) to the specified pin 156 | * Input : Pin and Value (0 to 255) 157 | * Output : None. 158 | * Return : 1 on success and a negative number on failure 159 | *******************************************************************************/ 160 | int tinkerAnalogWrite(String command) 161 | { 162 | //convert ascii to integer 163 | int pinNumber = command.charAt(1) - '0'; 164 | //Sanity check to see if the pin numbers are within limits 165 | if (pinNumber< 0 || pinNumber >7) return -1; 166 | 167 | String value = command.substring(3); 168 | 169 | if(command.startsWith("D")) 170 | { 171 | pinMode(pinNumber, OUTPUT); 172 | analogWrite(pinNumber, value.toInt()); 173 | return 1; 174 | } 175 | else if(command.startsWith("A")) 176 | { 177 | pinMode(pinNumber+10, OUTPUT); 178 | analogWrite(pinNumber+10, value.toInt()); 179 | return 1; 180 | } 181 | else return -2; 182 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/articfox1986/phpParticle.svg?branch=master)](https://travis-ci.org/articfox1986/phpParticle) 2 | 3 | phpParticle 4 | ======== 5 | 6 | PHP Class for interacting with the Particle Cloud (particle.io) 7 | 8 | ## Installation ## 9 | 10 | - GIT clone or download a zip of the repo and unzip into your project director 11 | - Rename `phpParticle.config.sample.php` to `phpParticle.config.php` 12 | - Set your access token and device id in `phpParticle.config.php` 13 | - (Optional) Copy and paste the code in `particle.firmware.cpp` into a new app in the Particle WebIDE & flash it to your core 14 | - (Optional) Run the any of the examples in the `examples` folder 15 | 16 | ## Usage 17 | 18 | - Check out the examples in the `examples` folder 19 | - Try out the [phpParticleDashboard](https://github.com/harrisonhjones/phpParticleDashboard) project which uses this project ([demo](http://projects.harrisonhjones.com/phpParticleDashboard/)) 20 | 21 | ## Implemented Features 22 | 23 | ### Device Management 24 | - List Devices 25 | - Get device info 26 | - Rename/Set device name 27 | - Call Particle Function on a device 28 | - Grab the value of a Particle Variable from a device 29 | - Remote (Over the Air) Firmware Uploads 30 | - Device signaling (make it flash a rainbow of colors) 31 | 32 | ### Access Token Management 33 | - Generate a new access token 34 | - List your access tokens 35 | - Delete an access token 36 | 37 | ### Webhook Management 38 | 39 | - List Webhooks 40 | - Add Webhook 41 | - Delete Webhook 42 | 43 | ### Account/Cloud Management 44 | - Use a local particle cloud 45 | - Claim core or photon 46 | - Remove core or photon 47 | 48 | ## Not Yet Implemented Features 49 | - OAuth Client Creation (/v1/clients) 50 | - Advanced OAuth topics -------------------------------------------------------------------------------- /src/ParticleAPI.php: -------------------------------------------------------------------------------- 1 | _endpoint = $endpoint; 37 | return true; 38 | } 39 | 40 | /** 41 | * Gets the API endpoint used 42 | * @return string 43 | */ 44 | public function getEndpoint() 45 | { 46 | return $this->_endpoint; 47 | } 48 | 49 | /** 50 | * Sets the timeout used for calls against the api. 51 | * 52 | * @param int $timeout The amount of time, in seconds, for a call to wait for data before returning with a TIMEOUT error 53 | * 54 | * @return void 55 | * 56 | */ 57 | public function setTimeout($timeout) 58 | { 59 | if(is_numeric($timeout)) 60 | { 61 | $this->_curlTimeout = intval($timeout); 62 | return true; 63 | } 64 | else 65 | { 66 | $errorText = "Non numeric timeout"; 67 | $this->_setError($errorText, __FUNCTION__); 68 | return false; 69 | } 70 | } 71 | 72 | /** 73 | * Gets the CURL timeout 74 | * @return double 75 | */ 76 | public function getTimeout() 77 | { 78 | return $this->_curlTimeout; 79 | } 80 | 81 | /** 82 | * Sets the authentication details for authenticating with the API 83 | * 84 | * @param string $email The email to authenticate with 85 | * @param string $password The password to authenticate with 86 | * 87 | * @return void 88 | * 89 | */ 90 | public function setAuth($email, $password) 91 | { 92 | $this->_email = $email; 93 | $this->_password = $password; 94 | return true; 95 | } 96 | 97 | /** 98 | * Gets the auth email 99 | * @return string 100 | */ 101 | public function getEmail() 102 | { 103 | return $this->_email; 104 | } 105 | 106 | /** 107 | * Gets the auth password 108 | * @return string 109 | */ 110 | public function getPassword() 111 | { 112 | return $this->_password; 113 | } 114 | 115 | /** 116 | * Clears all the authentication info (email and password). Internally set to false. Subsequent calls which require a email/password will fail 117 | * 118 | * @return void 119 | * 120 | */ 121 | public function clearAuth() 122 | { 123 | $this->setAuth(false,false); 124 | return true; 125 | } 126 | 127 | /** 128 | * Sets the access token for authenticating with the API 129 | * 130 | * @param string $accessToken The access token to authenticate with 131 | * 132 | * @return void 133 | * 134 | */ 135 | public function setAccessToken($accessToken) 136 | { 137 | $this->_accessToken = $accessToken; 138 | return true; 139 | } 140 | 141 | /** 142 | * Gets the Access Token 143 | * @return string 144 | */ 145 | public function getAccessToken() 146 | { 147 | return $this->_accessToken; 148 | } 149 | 150 | /** 151 | * Clears the access token info. Internally set to false. Subsequent calls which require an access token will fail 152 | * 153 | * @return void 154 | * 155 | */ 156 | public function clearAccessToken() 157 | { 158 | $this->setAccessToken(false); 159 | return true; 160 | } 161 | 162 | /** 163 | * Sets the debug type. Use "HTML" for errors automatically formatted for embedding into a webpage and "TEXT" for unformatted raw errors 164 | * 165 | * @param string $debugType The debug type (either "HTML" or "TEXT") 166 | * 167 | * @return void 168 | * 169 | */ 170 | public function setDebugType($debugType = "HTML") 171 | { 172 | if(($debugType == "HTML") or ($debugType == "TEXT")) 173 | { 174 | $this->_debugType = $debugType; 175 | return true; 176 | } 177 | else 178 | { 179 | $this->_setError("Bad debut type (" . $debugType . ")", "setDebugType"); 180 | return false; 181 | } 182 | } 183 | 184 | /** 185 | * Gets the debug type 186 | * @return string 187 | */ 188 | public function getDebugType() 189 | { 190 | return $this->_debugType; 191 | } 192 | 193 | /** 194 | * Turn internal debugging on or off. Note, external calls made to debug ($obj->debug(...)) will always display regardless of this setting 195 | * 196 | * @param boolean $debug true turns on internal debugging & false turns off internal debugging 197 | * 198 | * @return void 199 | * 200 | */ 201 | public function setDebug($debug = false) 202 | { 203 | $this->_debug = ($debug) ? true : false; 204 | return true; 205 | } 206 | 207 | /** 208 | * Gets whether debug is on or off 209 | * @return boolean 210 | */ 211 | public function getDebug() 212 | { 213 | return $this->_debug; 214 | } 215 | 216 | /** 217 | * Turn on or off SSL verification (it's a CURL thing). For testing, before you get the certificates setup, you might need to disable SSL verificatioon. Note this is a security concern 218 | * 219 | * @param boolean $disableSSL true allows you to communicate with api endpoints with invalid security certificates & false enforces SSL verification 220 | * 221 | * @return void 222 | * 223 | */ 224 | public function setDisableSSL($disableSSL = false) 225 | { 226 | $this->_disableSSL = ($disableSSL) ? true : false; 227 | return true; 228 | } 229 | 230 | /** 231 | * Gets the whether ssls are disabled 232 | * @return string 233 | */ 234 | public function getDisableSSL() 235 | { 236 | return $this->_disableSSL; 237 | } 238 | 239 | /** 240 | * Private Function. Sets the internal _error & _errorSource variables. Allow for tracking which function resulted in an error and what that error was 241 | * 242 | * @param string $errorText The value to set _error to 243 | * @param string $errorSource The value to set _errorSource to 244 | * 245 | * @return void 246 | * 247 | */ 248 | private function _setError($errorText, $errorSource) 249 | { 250 | $this->_error = $errorText; 251 | $this->_errorSource = $errorSource; 252 | } 253 | 254 | /** 255 | * Private Function. Sets the internal _errorSource. Allow for tracking which function resulted in an error 256 | * 257 | * @param string $errorSource The value to set _errorSource to 258 | * 259 | * @return void 260 | * 261 | */ 262 | private function _setErrorSource($errorSource) 263 | { 264 | $this->_errorSource = $errorSource; 265 | } 266 | 267 | /** 268 | * Set the product slug. 269 | * 270 | * @param string $productSlug The product slug to use. 271 | * 272 | * @return void 273 | * 274 | */ 275 | public function setProductSlug($productSlug) 276 | { 277 | $this->_productSlug = $productSlug; 278 | return true; 279 | } 280 | 281 | /** 282 | * Gets the current product slug. 283 | * @return string 284 | */ 285 | public function getProductSlug() 286 | { 287 | return $this->_productSlug; 288 | } 289 | 290 | /** 291 | * Private Function. Outputs the desired debug text formatted if required 292 | * 293 | * @param string $debugText The debug string to output 294 | * @param string $override If set to true overrides the internal debug on/off state and always outputs the debugText. If set to false it follows the internal debug on/off state 295 | * 296 | * @return void 297 | * 298 | */ 299 | private function _debug($debugText, $override = false) 300 | { 301 | if(($this->_debug == true) || ($override == true)) 302 | { 303 | if($this->_debugType == "HTML") 304 | { 305 | echo $debugText . "
\n"; 306 | return true; 307 | } 308 | else if($this->_debugType == "TEXT") 309 | { 310 | echo $debugText . "\n"; 311 | return true; 312 | } 313 | else 314 | { 315 | $this->_setError("Bad debut type (" . $this->_debugType . ")", "_debug"); 316 | return false; 317 | } 318 | } 319 | } 320 | 321 | /** 322 | * Private Function. Outputs the desired debug array formatted if required 323 | * 324 | * @param mixed[] $debugArray The debug array to output 325 | * @param string $override If set to true overrides the internal debug on/off state and always outputs the debugArray. If set to false it follows the internal debug on/off state 326 | * 327 | * @return void 328 | * 329 | */ 330 | private function _debug_r($debugArray, $override = false) 331 | { 332 | if(($this->_debug == true) || ($override == true)) 333 | { 334 | if($this->_debugType == "HTML") 335 | { 336 | $this->debug("
");
337 |                 print_r($debugArray);
338 |                 $this->debug("
"); 339 | return true; 340 | } 341 | else if($this->_debugType == "TEXT") 342 | { 343 | print_r($debugArray); 344 | $this->debug(); 345 | return true; 346 | } 347 | else 348 | { 349 | $this->_setError("Bad debut type (" . $this->_debugType . ")", "_debug"); 350 | return false; 351 | } 352 | } 353 | } 354 | 355 | /** 356 | * Outputs the desired debug text formatted if required 357 | * 358 | * @param string $debugText The debug string to output 359 | * 360 | * @return void 361 | * 362 | */ 363 | public function debug($debugText) 364 | { 365 | return $this->_debug($debugText, $override = true); 366 | } 367 | 368 | /** 369 | * Outputs the desired debug array formatted if required 370 | * 371 | * @param string $debugArray The debug array to output 372 | * 373 | * @return void 374 | * 375 | */ 376 | public function debug_r($debugArray) 377 | { 378 | return $this->_debug_r($debugArray, $override = true); 379 | } 380 | 381 | /** 382 | * Runs a particle function on the device. Requires the accessToken to be set 383 | * 384 | * @param string $deviceID The device ID of the device to call the function on 385 | * @param string $deviceFunction The name function to call 386 | * @param string $params The parameters to send to the function (the 'args') 387 | * 388 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 389 | */ 390 | public function callFunction($deviceID, $deviceFunction, $params) 391 | { 392 | $url = $this->_endpoint .'v1/'; 393 | if(empty($this->_productSlug)) { 394 | $url .= 'devices/' . $deviceID . '/' . $deviceFunction; 395 | } else { 396 | $url .= 'products/' . $this->_productSlug . '/devices/' . $deviceID . '/' . $deviceFunction; 397 | } 398 | return $this->_curlRequest($url, array('args'=>$params), 'post'); 399 | } 400 | 401 | /** 402 | * Gets the value of a particle variable. Requires the accessToken to be set 403 | * 404 | * @param string $deviceID The device ID of the device to call the function on 405 | * @param string $variableName The name of the variable to retrieve 406 | * 407 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 408 | */ 409 | public function getVariable($deviceID, $variableName) 410 | { 411 | $url = $this->_endpoint .'v1/'; 412 | if(empty($this->_productSlug)) { 413 | $url .= 'devices/' . $deviceID . '/' . $variableName; 414 | } else { 415 | $url .= 'products/' . $this->_productSlug . '/devices/' . $deviceID . '/' . $variableName; 416 | } 417 | return $this->_curlRequest($url, array(), 'get'); 418 | } 419 | 420 | /** 421 | * Lists all your cores assigned to your cloud account. Requires the accessToken to be set 422 | * 423 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 424 | */ 425 | public function listDevices() 426 | { 427 | $url = $this->_endpoint .'v1/'; 428 | if(empty($this->_productSlug)) { 429 | $url .= 'devices/'; 430 | } else { 431 | $url .= 'products/' . $this->_productSlug . '/devices/'; 432 | } 433 | return $this->_curlRequest($url, array(), 'get'); 434 | } 435 | 436 | /** 437 | * Gets your details from your core e.g. function and variables. Requires the accessToken to be set 438 | * 439 | * @param string $deviceID The device ID of the device 440 | * 441 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 442 | */ 443 | public function getAttributes($deviceID) 444 | { 445 | $url = $this->_endpoint .'v1/'; 446 | if(empty($this->_productSlug)) { 447 | $url .= 'devices/' . $deviceID; 448 | } else { 449 | $url .= 'products/' . $this->_productSlug . '/devices/' . $deviceID; 450 | } 451 | return $this->_curlRequest($url, array(), 'get'); 452 | } 453 | 454 | /** 455 | * Set the name/renames your core. Requires the accessToken to be set 456 | * 457 | * @param string $deviceID The device ID of the device to rename 458 | * @param string $name The new name of the device 459 | * 460 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 461 | */ 462 | public function renameDevice($deviceID,$name) 463 | { 464 | $url = $this->_endpoint .'v1/'; 465 | if(empty($this->_productSlug)) { 466 | $url .= 'devices/' . $deviceID; 467 | } else { 468 | $url .= 'products/' . $this->_productSlug . '/devices/' . $deviceID; 469 | } 470 | return $this->_curlRequest($url, array("name" => $name), 'put'); 471 | } 472 | 473 | /** 474 | * Attempts to add a device to your cloud account. Requires the accessToken to be set. Note, you may want to follow this up with a call to "setName" as new Core's names are blank. Interestingly, if claiming an order core their name is retained across the unclaim/claim process 475 | * 476 | * @param string $deviceID The device ID of the device to claim. 477 | * @param boolean $requestTransfer If true requests that the device be transfered to your account (use if the device is already claimed). If false will try to claim but not automatically send a transfer request 478 | * 479 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 480 | */ 481 | 482 | public function claimDevice($deviceID, $requestTransfer = false) 483 | { 484 | $url = $this->_endpoint .'v1/'; 485 | if(empty($this->_productSlug)) { 486 | $url .= 'devices'; 487 | } else { 488 | $url .= 'products/' . $this->_productSlug . '/devices'; 489 | } 490 | if($requestTransfer) 491 | $result = $this->_curlRequest($url, array('id' => $deviceID, 'request_transfer' => 'true'), 'post'); 492 | else 493 | $result = $this->_curlRequest($url, array('id' => $deviceID, 'request_transfer' => 'false'), 'post'); 494 | 495 | return $result; 496 | } 497 | 498 | /** 499 | * Removes the core from your cloud account. Requires the accessToken to be set 500 | * 501 | * @param string $deviceID The device ID of the device to remove from your account. 502 | * 503 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 504 | */ 505 | public function removeDevice($deviceID) 506 | { 507 | $url = $this->_endpoint .'v1/'; 508 | if(empty($this->_productSlug)) { 509 | $url .= 'devices/' . $deviceID; 510 | } else { 511 | $url .= 'products/' . $this->_productSlug . '/devices/' . $deviceID; 512 | } 513 | return $this->_curlRequest($url, array(), 'delete'); 514 | } 515 | 516 | /** 517 | * Uploads a sketch to a Particle device. Requires the accessToken to be set 518 | * 519 | * @param string $deviceID The device ID of the device to upload the code to 520 | * @param string $filename The filename of the firmware file to upload to the device. Ex: tinker.cpp. Not yet implemented 521 | * @param string $filepath The path to the firmware file to upload (including the name). Ex: path/to/tinker.cpp 522 | * @param boolean $isBinary Set to true if uploading a .bin file or false otherwise. 523 | * 524 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 525 | */ 526 | public function uploadFirmware($deviceID,$filename,$filepath,$isBinary=false) 527 | { 528 | // Create a CURLFile object 529 | $cfile = new CURLFile($filepath,'application/octet-stream',$filename); 530 | 531 | $url = $this->_endpoint .'v1/'; 532 | if(empty($this->_productSlug)) { 533 | $url .= 'devices/' . $deviceID; 534 | } else { 535 | $url .= 'products/' . $this->_productSlug . '/devices/' . $deviceID; 536 | } 537 | 538 | $params = array('file' => $cfile); 539 | if($isBinary == true) 540 | $params['file_type'] = "binary"; 541 | $result = $this->_curlRequest($url, $params, 'put-file'); 542 | return $result; 543 | } 544 | 545 | /** 546 | * Gets a list of your tokens from the particle cloud. Requires the email/password auth to be set 547 | * 548 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 549 | */ 550 | public function listAccessTokens() 551 | { 552 | $url = $this->_endpoint .'v1/access_tokens'; 553 | return $this->_curlRequest($url, array(), 'get', 'basic'); 554 | } 555 | 556 | /** 557 | * Creates a new token on the particle cloud. Requires the email/password auth to be set 558 | * 559 | * @param int $expires_in When the token should expire (in seconds). Set to false to ignore and use the default. Set to 0 for a token that never expires 560 | * @param string $expires_at When the token should expire (at a date/time). Set to false to ignore and use the default. Set to 'null' for a token that never expires. Otherwise this should be a ISO8601 style date string 561 | * @param string $clientID The clientID. If you don't have one of these (only used in OAuth applications) set to false 562 | * @param string $clientSecret The clientSecret. If you don't have one of these (only used in OAuth applications) set to false 563 | * 564 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 565 | */ 566 | 567 | public function newAccessToken($expires_in = false, $expires_at = false, $clientID = false, $clientSecret = false) 568 | { 569 | $fields = array('grant_type' => 'password', 'username' => $this->_email, 'password' => $this->_password); 570 | 571 | if($expires_in !== false) 572 | $fields['expires_in'] = intval($expires_in); 573 | 574 | if($expires_at !== false) 575 | $fields['expires_at'] = $expires_at; 576 | 577 | if($clientID) 578 | { 579 | $fields['client_id'] = $clientID; 580 | $fields['client_secret'] = $clientSecret; 581 | } 582 | 583 | $url = $this->_endpoint .'oauth/token'; 584 | $result = $this->_curlRequest($url, $fields, 'post', 'basic-dummy'); 585 | 586 | return $result; 587 | } 588 | 589 | /** 590 | * Removes the token from the particle cloud. Requires the email/password auth to be set 591 | * 592 | * @param string $token The access token to remove 593 | * 594 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 595 | */ 596 | public function deleteAccessToken($token) 597 | { 598 | $url = $this->_endpoint .'v1/access_tokens/'.$token; 599 | $result = $this->_curlRequest($url, array(), 'delete', 'basic'); 600 | 601 | return $result; 602 | } 603 | 604 | /** 605 | * Gets a list of webhooks from the particle cloud. Requires the accessToken to be set 606 | * 607 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 608 | */ 609 | public function listWebhooks() 610 | { 611 | $fields = array(); 612 | $url = $this->_endpoint .'v1/webhooks'; 613 | $result = $this->_curlRequest($url, $fields, 'get'); 614 | 615 | return $result; 616 | } 617 | 618 | /** 619 | * Creates a new webhook on the particle cloud. Requires the accessToken to be set 620 | * @param string $event The event name used to trigger the webhook 621 | * @param string $webhookUrl The url to query once the event has occured 622 | * @param string $extras See http://docs.particle.io/webhooks/#webhook-options 623 | * 624 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 625 | */ 626 | public function newWebhook($event, $webhookUrl, $extras = array()) 627 | { 628 | $url = $this->_endpoint .'v1/webhooks/'; 629 | $fields = array_merge(array('event' => $event, 'url' => $webhookUrl),$extras); 630 | $result = $this->_curlRequest($url, $fields , 'post'); 631 | return $result; 632 | } 633 | 634 | /** 635 | * Delete webhooks from the particle cloud. Requires the accessToken to be set 636 | * 637 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 638 | */ 639 | public function deleteWebhook($webhookID) 640 | { 641 | $fields = array(); 642 | $url = $this->_endpoint ."v1/webhooks/{$webhookID}/"; 643 | $result = $this->_curlRequest($url, $fields, 'delete'); 644 | 645 | return $result; 646 | } 647 | 648 | /** 649 | * Sets the particle core signal mode state. Requires the accessToken to be set 650 | * 651 | * @param string $deviceID The device ID of the device to send the signal mode state change command to. 652 | * @param int $signalState The signal state: 0 returns the RGB led back to normmal & 1 makes it flash a rainbow of color 653 | * 654 | * @return boolean true if the call was successful, false otherwise. Use getResult to get the api result and use getError & getErrorSource to determine what happened in the event of an error 655 | */ 656 | public function signalDevice($deviceID, $signalState = 0) 657 | { 658 | $url = $this->_endpoint .'v1/'; 659 | if(empty($this->_productSlug)) { 660 | $url .= 'devices/' . $deviceID; 661 | } else { 662 | $url .= 'products/' . $this->_productSlug . '/devices/' . $deviceID; 663 | } 664 | $fields = array('signal' => $signalState); 665 | //$url = $this->_endpoint ."v1/devices/{$deviceID}/"; 666 | return $this->_curlRequest($url, $fields, 'put'); 667 | } 668 | 669 | /** 670 | * Returns the latest error 671 | * 672 | * @return string The latest error 673 | */ 674 | public function getError() 675 | { 676 | return $this->_error; 677 | } 678 | 679 | /** 680 | * Returns the latest error's source (which function cause the error) 681 | * 682 | * @return string The latest error's source 683 | */ 684 | public function getErrorSource() 685 | { 686 | return $this->_errorSource; 687 | } 688 | 689 | /** 690 | * Returns the latest result 691 | * 692 | * @return string The latest result from calling a cloud function 693 | */ 694 | public function getResult() 695 | { 696 | return $this->_result; 697 | } 698 | 699 | /** 700 | * Private Function. Performs a CURL Request with the given parameters 701 | * 702 | * @param string url The url to call 703 | * @param mixed[] params An array of parameters to pass to the url 704 | * @param string type The type of request ("GET", "POST", "PUT", etc) 705 | * @param string authType The type of authorization to use ('none' uses the access token, 'basic' uses basic auth with the email/password auth details, and 'basic-dummy' uses dummy basic auth details) 706 | * 707 | * @return boolean true on success, false on failure 708 | */ 709 | private function _curlRequest($url, $params = null, $type = 'post', $authType = 'none') 710 | { 711 | 712 | $fields_string = null; 713 | 714 | if($authType == 'none') 715 | if ($this->_accessToken) 716 | { 717 | $params['access_token'] = $this->_accessToken; 718 | } 719 | else 720 | { 721 | $errorText = "No access token set"; 722 | list(, $caller) = debug_backtrace(false); 723 | $this->_setError($errorText, $caller['function']); 724 | return false; 725 | } 726 | 727 | 728 | // is cURL installed yet? 729 | if (!function_exists('curl_init')) 730 | { 731 | die("CURL is not installed/available"); 732 | } 733 | 734 | // OK cool - then let's create a new cURL resource handle 735 | $ch = curl_init(); 736 | //set the number of POST vars, POST data 737 | if($type == 'get') 738 | { 739 | $url .= ("?" . http_build_query($params)); 740 | } 741 | else if ($type == 'post') 742 | { 743 | curl_setopt($ch,CURLOPT_POST,count($params)); 744 | curl_setopt($ch,CURLOPT_POSTFIELDS,http_build_query($params)); 745 | } 746 | else if($type == "put") 747 | { 748 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); 749 | curl_setopt($ch,CURLOPT_POSTFIELDS,http_build_query($params)); 750 | } 751 | else if($type == "put-file") 752 | { 753 | curl_setopt($ch, CURLOPT_POST, true); 754 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); 755 | unset($params['access_token']); 756 | curl_setopt($ch,CURLOPT_POSTFIELDS,$params); 757 | $url .= "?access_token=" . $this->_accessToken; 758 | } 759 | else if ($type == 'delete') 760 | { 761 | $url .= ("?" . http_build_query($params)); 762 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); 763 | } 764 | else 765 | { 766 | $errorText = "Unsupported method type (" . $type . ")"; 767 | $this->_setError($errorText, __FUNCTION__); 768 | return false; 769 | } 770 | 771 | $this->_debug("Opening a {$type} connection to {$url}"); 772 | curl_setopt($ch, CURLOPT_URL, $url); 773 | 774 | if($this->_disableSSL) 775 | { 776 | // stop the verification of certificate 777 | $this->_debug("[WARN] Disabling SSL Verification for CURL"); 778 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 779 | } 780 | 781 | // Set a referer 782 | // curl_setopt($ch, CURLOPT_REFERER, "http://www.example.com/curl.htm"); 783 | 784 | // User agent 785 | // curl_setopt($ch, CURLOPT_USERAGENT, "MozillaXYZ/1.0"); 786 | 787 | // Include header in result? (0 = yes, 1 = no) 788 | curl_setopt($ch, CURLOPT_HEADER, 0); 789 | 790 | // Should cURL return or print out the data? (true = return, false = print) 791 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 792 | 793 | // Timeout in seconds 794 | curl_setopt($ch, CURLOPT_TIMEOUT, $this->_curlTimeout); 795 | 796 | $this->_debug("Auth Type: " . $authType); 797 | // basic auth 798 | if ($authType == 'basic') { 799 | if(($this->_email) && ($this->_password)) 800 | { 801 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 802 | curl_setopt($ch, CURLOPT_USERPWD, $this->_email . ":" . $this->_password); 803 | } 804 | else 805 | { 806 | list(, $caller) = debug_backtrace(false); 807 | $errorText = "No auth credentials (email/password) set"; 808 | $this->_setError($errorText, $caller['function']); 809 | return false; 810 | } 811 | } 812 | if ($authType == 'basic-dummy') { 813 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 814 | curl_setopt($ch, CURLOPT_USERPWD, "particle:particle"); 815 | } 816 | 817 | // Download the given URL, and return output 818 | $this->_debug("Executing Curl Operation"); 819 | $this->_debug("Url:"); 820 | $this->_debug_r($url); 821 | $this->_debug("Params:"); 822 | $this->_debug_r($params); 823 | $output = curl_exec($ch); 824 | 825 | $this->_debug("Curl Result: '" . $output . "'"); 826 | 827 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 828 | $this->_debug("Curl Response Code: '" . $httpCode."'"); 829 | // Close the cURL resource, and free system resources 830 | 831 | $curlError = curl_errno($ch); 832 | curl_close($ch); 833 | if($curlError != CURLE_OK) 834 | { 835 | $this->_debug("CURL Request - There was a CURL error"); 836 | list(, $caller) = debug_backtrace(false); 837 | //var_dump($caller['function']); 838 | $errorText = $this->_curlErrorCode($curlError); 839 | $this->_setError($errorText, $caller['function']); 840 | return false; 841 | } 842 | else 843 | { 844 | $retVal = json_decode($output,true); 845 | 846 | if(json_last_error() == 0) 847 | { 848 | if(isset($retVal['error']) && $retVal['error']) 849 | { 850 | $this->_debug("CURL Request - API response contained 'error' field"); 851 | $errorText = $retVal['error']; 852 | $this->_setError($errorText, __FUNCTION__); 853 | return false; 854 | } 855 | else 856 | { 857 | $this->_debug("CURL Request - Returning True"); 858 | $this->_result = $retVal; 859 | return true; 860 | } 861 | } 862 | else 863 | { 864 | $this->_debug("CURL Request - Unable to parse JSON"); 865 | $errorText = "Unable to parse JSON. Json error = " . json_last_error() . ". See http://php.net/manual/en/function.json-last-error.php for more information. Raw response from Particle Cloud = '" . $result . "'"; 866 | $this->_setError($errorText, __FUNCTION__); 867 | return false; 868 | } 869 | } 870 | } 871 | 872 | /** 873 | * Private Function. Returns a human readable string for a given CURL Error Code 874 | * 875 | * @param int curlCode The CURL error code 876 | * 877 | * @return string A human-readable string version of the curlCode 878 | */ 879 | private function _curlErrorCode($curlCode) 880 | { 881 | switch ($curlCode) 882 | { 883 | case 26: 884 | return "Curl Error. There was a problem reading a local file or an error returned by the read callback."; 885 | case 30: 886 | return "Curl Error. Operation timeout. The specified time-out period was reached according to the conditions."; 887 | default: 888 | return "Curl Error. Error number = {$curlCode}. See http://curl.haxx.se/libcurl/c/libcurl-errors.html for more information"; 889 | } 890 | } 891 | 892 | } -------------------------------------------------------------------------------- /tests/ParticleAPITest.php: -------------------------------------------------------------------------------- 1 | setEndpoint("https://api.particle.io/"); 11 | 12 | $this->assertEquals(true,$result); 13 | $this->assertEquals("https://api.particle.io/",$particle->getEndpoint()); 14 | } 15 | 16 | public function test_setting_timeout() 17 | { 18 | $particle = new ParticleAPI; 19 | $result = $particle->setTimeout(15); 20 | 21 | $this->assertEquals(true,$result); 22 | $this->assertEquals(15,$particle->getTimeout()); 23 | } 24 | 25 | public function test_setting_non_numeric_value_for_timeout() 26 | { 27 | $particle = new ParticleAPI; 28 | $result = $particle->setTimeout('test'); 29 | 30 | $this->assertEquals(false,$result); 31 | $this->assertEquals('Non numeric timeout',$particle->getError()); 32 | $this->assertEquals('setTimeout',$particle->getErrorSource()); 33 | } 34 | 35 | public function test_setting_auth() 36 | { 37 | $particle = new ParticleAPI; 38 | $result = $particle->setAuth('test@test.com','password'); 39 | 40 | $this->assertEquals(true,$result); 41 | $this->assertEquals('test@test.com',$particle->getEmail()); 42 | $this->assertEquals('password',$particle->getPassword()); 43 | } 44 | 45 | public function test_clearing_auth() 46 | { 47 | $particle = new ParticleAPI; 48 | $result = $particle->clearAuth(); 49 | 50 | $this->assertEquals(true,$result); 51 | $this->assertEquals(false, $particle->getEmail()); 52 | $this->assertEquals(false,$particle->getPassword()); 53 | } 54 | 55 | public function test_setting_access_token() 56 | { 57 | $particle = new ParticleAPI; 58 | $result = $particle->setAccessToken('a5a7b2d620fa349c8e825f02a6513de6ca7baabb'); 59 | 60 | $this->assertEquals(true,$result); 61 | $this->assertEquals('a5a7b2d620fa349c8e825f02a6513de6ca7baabb',$particle->getAccessToken()); 62 | } 63 | 64 | public function test_clearing_access_token() 65 | { 66 | $particle = new ParticleAPI; 67 | $result = $particle->clearAccessToken(); 68 | 69 | $this->assertEquals(true,$result); 70 | $this->assertEquals(false,$particle->getAccessToken()); 71 | } 72 | 73 | public function test_setting_debug_type() 74 | { 75 | $particle = new ParticleAPI; 76 | $result = $particle->setDebugType('TEXT'); 77 | 78 | $this->assertEquals(true,$result); 79 | $this->assertEquals('TEXT',$particle->getDebugType()); 80 | } 81 | 82 | public function test_setting_debug_type_invalid() 83 | { 84 | $particle = new ParticleAPI; 85 | $result = $particle->setDebugType('SomethingStrange'); 86 | 87 | $this->assertEquals(false,$result); 88 | $this->assertContains('Bad debut type',$particle->getError()); 89 | $this->assertEquals('HTML',$particle->getDebugType()); 90 | } 91 | 92 | public function test_setting_debug_to_true() 93 | { 94 | $particle = new ParticleAPI; 95 | $result = $particle->setDebug(true); 96 | 97 | $this->assertEquals(true,$result); 98 | $this->assertEquals(true,$particle->getDebug()); 99 | } 100 | 101 | public function test_setting_disable_ssl_to_true() 102 | { 103 | $particle = new ParticleAPI; 104 | $result = $particle->setDisableSSL(true); 105 | 106 | $this->assertEquals(true,$result); 107 | $this->assertEquals(true,$particle->getDisableSSL()); 108 | } 109 | 110 | public function test_setting_debug_message() 111 | { 112 | $particle = new ParticleAPI; 113 | $result = $particle->debug('debugging text'); 114 | 115 | $this->assertEquals(true,$result); 116 | } 117 | 118 | 119 | } --------------------------------------------------------------------------------