├── .github └── FUNDING.yml ├── .gitignore ├── .ipynb_checkpoints └── development-checkpoint.ipynb ├── LICENSE ├── README.md ├── bird_project ├── 5b0ce5d8023d4e35.classificationbox ├── HA_motion_camera_view.png ├── README.md ├── bird.jpg ├── bird_not_bird_examples.png ├── bird_project.pptx ├── hassio_addons.png ├── iphone_notification.jpeg ├── magpi.png ├── not_bird.jpg ├── sequence.png ├── setup.png └── system_overview.png ├── custom_components └── classificationbox │ ├── __init__.py │ ├── image_processing.py │ └── manifest.json ├── development.ipynb ├── tests └── test_classificationbox.py └── usage.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: robmarkcole 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin/ 3 | bin-debug/ 4 | bin-release/ 5 | [Oo]bj/ # FlashDevelop obj 6 | [Bb]in/ # FlashDevelop bin 7 | 8 | # Other files and folders 9 | .settings/ 10 | 11 | # Executables 12 | *.swf 13 | *.air 14 | *.ipa 15 | *.apk 16 | 17 | .ipynb_checkpoints 18 | 19 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` 20 | # should NOT be excluded as they contain compiler settings and other important 21 | # information for Eclipse / Flash Builder. 22 | -------------------------------------------------------------------------------- /.ipynb_checkpoints/development-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Have trained a classifier following https://blog.machinebox.io/how-anyone-can-build-a-machine-learning-image-classifier-from-photos-on-your-hard-drive-very-5c20c6f2764f using my own bird data. Achieved an accuracy of 88% - but the first time I ran the script (different random selection of images) accuracy was about 92%, so clearly I am very sensitive to the images randomly selected and should add more images:\n", 8 | "```\n", 9 | "Correct: 180\n", 10 | "Incorrect: 23\n", 11 | "Errors: 0\n", 12 | "Accuracy: 88.66995073891626%\n", 13 | "```\n", 14 | "\n", 15 | "https://machineboxio.com/docs/classificationbox\n", 16 | "\n", 17 | "classificationbox is online learning (supervised learning), it works also with little data but it build the classifier function so it needs more data. for my bird monirtoring project my 2 classes would be bird/no_bird, and I just post the image data\n", 18 | "\n", 19 | "Classifiers can be made to help solve a wide range of example use cases, for example:\n", 20 | "\n", 21 | "* Learn about how your company is perceived by grouping tweets into positive and negative\n", 22 | "* Automatically group photos of cats and dogs\n", 23 | "* Group emails into spam and non-spam categories\n", 24 | "* Build a classifier to detect the language of a piece of text based on previously taught examples\n", 25 | "\n", 26 | "```\n", 27 | "sudo docker pull machinebox/classificationbox\n", 28 | "\n", 29 | "sudo docker run -p 8080:8080 -e \"MB_KEY=$MB_KEY\" machinebox/classificationbox\n", 30 | "```\n", 31 | "http://localhost:8080/\n", 32 | "\n", 33 | "Note that if you restart classificationbox models will be lost" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 1, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "import requests\n", 43 | "# import curlify\n", 44 | "import re\n", 45 | "import json\n", 46 | "import base64\n", 47 | "import matplotlib.pyplot as plt\n", 48 | "import matplotlib.patches as patches\n", 49 | "%matplotlib inline\n", 50 | "\n", 51 | "def base64_encode_file(file_path):\n", 52 | " \"\"\"\n", 53 | " Takes the path to an image and returns the base64 encoded\n", 54 | " image data as a string.\n", 55 | " \"\"\"\n", 56 | " with open(file_path, \"rb\") as f:\n", 57 | " file_data = base64.b64encode(f.read()).decode('ascii')\n", 58 | " return file_data\n", 59 | "\n", 60 | "ATTR_ID = 'id'\n", 61 | "ATTR_CONFIDENCE = 'confidence'" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 25, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "IP = 'localhost'\n", 71 | "#IP = '192.168.0.30'\n", 72 | "PORT = '8080'\n", 73 | "CLASSIFIER = 'classificationbox'\n", 74 | "CONFIDENCE = 80\n", 75 | "\n", 76 | "MODELS_URL = 'http://{}:{}/{}/models'.format(IP, PORT, CLASSIFIER)\n", 77 | "MODEL_CREATION_URL = 'http://{}:{}/{}/models'.format(IP, PORT, CLASSIFIER)\n", 78 | "STATE_POST_URL = 'http://{}:{}/classificationbox/state'.format(IP, PORT)\n", 79 | "\n", 80 | "username = 'my_username'\n", 81 | "password = 'my_password'\n", 82 | "kwargs = {}\n", 83 | "if username:\n", 84 | " kwargs['auth'] = requests.auth.HTTPBasicAuth(username, password)" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 26, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "text/plain": [ 95 | "'http://localhost:8080/classificationbox/models'" 96 | ] 97 | }, 98 | "execution_count": 26, 99 | "metadata": {}, 100 | "output_type": "execute_result" 101 | } 102 | ], 103 | "source": [ 104 | "MODELS_URL" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 27, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "def get_models(url):\n", 114 | " \"\"\"Return the list of models.\"\"\"\n", 115 | " try:\n", 116 | " response = requests.get(url, **kwargs)\n", 117 | " response_json = response.json()\n", 118 | " if response_json['success']:\n", 119 | " if len(response_json['models']) == 0:\n", 120 | " print(\"%s error: No models found\", CLASSIFIER)\n", 121 | " else:\n", 122 | " return response_json['models']\n", 123 | " except requests.exceptions.ConnectionError:\n", 124 | " print(\"ConnectionError: Is %s running?\", CLASSIFIER)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 28, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "data": { 134 | "text/plain": [ 135 | "{'auth': }" 136 | ] 137 | }, 138 | "execution_count": 28, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "kwargs" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 31, 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "data": { 154 | "text/plain": [ 155 | "{'success': True,\n", 156 | " 'models': [{'id': '5b0ce5d8023d4e35', 'name': '5b0ce5d8023d4e35'}]}" 157 | ] 158 | }, 159 | "execution_count": 31, 160 | "metadata": {}, 161 | "output_type": "execute_result" 162 | } 163 | ], 164 | "source": [ 165 | "response = requests.get(MODELS_URL, timeout=9, **kwargs)\n", 166 | "response_json = response.json()\n", 167 | "response_json" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "Lets list my models" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 6, 180 | "metadata": {}, 181 | "outputs": [ 182 | { 183 | "data": { 184 | "text/plain": [ 185 | "[{'id': '5b0ce5d8023d4e35', 'name': '5b0ce5d8023d4e35'}]" 186 | ] 187 | }, 188 | "execution_count": 6, 189 | "metadata": {}, 190 | "output_type": "execute_result" 191 | } 192 | ], 193 | "source": [ 194 | "models = get_models(MODELS_URL)\n", 195 | "models" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 7, 201 | "metadata": {}, 202 | "outputs": [ 203 | { 204 | "name": "stdout", 205 | "output_type": "stream", 206 | "text": [ 207 | "5b0ce5d8023d4e35 5b0ce5d8023d4e35\n" 208 | ] 209 | } 210 | ], 211 | "source": [ 212 | "for model in models:\n", 213 | " print(model['id'], model['name'])" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": {}, 219 | "source": [ 220 | "I want the model which was generated by the GO script on my bird data" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 16, 226 | "metadata": {}, 227 | "outputs": [ 228 | { 229 | "data": { 230 | "text/plain": [ 231 | "'5b0ce5d8023d4e35'" 232 | ] 233 | }, 234 | "execution_count": 16, 235 | "metadata": {}, 236 | "output_type": "execute_result" 237 | } 238 | ], 239 | "source": [ 240 | "MODEL_ID = models[0]['id']\n", 241 | "MODEL_ID" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": 17, 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "MODEL_PREDICT_URL = 'http://{}:{}/{}/models/{}/predict'.format(IP, PORT, CLASSIFIER, MODEL_ID)\n", 251 | "MODEL_STATS_URL = 'http://{}:{}/{}/models/{}/stats'.format(IP, PORT, CLASSIFIER, MODEL_ID)\n", 252 | "\n", 253 | "MODEL_STATE_URL = 'http://{}:{}/classificationbox/state/{}'.format(IP, PORT, MODEL_ID)\n", 254 | "MODEL_TEACH_URL = 'http://{}:{}/{}/models/{}/teach'.format(IP, PORT, CLASSIFIER, MODEL_ID)" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "Now lets see the stats on this model" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 18, 267 | "metadata": {}, 268 | "outputs": [ 269 | { 270 | "data": { 271 | "text/plain": [ 272 | "{'success': True, 'predictions': 1, 'examples': 0, 'classes': []}" 273 | ] 274 | }, 275 | "execution_count": 18, 276 | "metadata": {}, 277 | "output_type": "execute_result" 278 | } 279 | ], 280 | "source": [ 281 | "model_stats = requests.get(MODEL_STATS_URL).json()\n", 282 | "model_stats" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "Lets make a prediction on a bird image" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": 19, 295 | "metadata": {}, 296 | "outputs": [ 297 | { 298 | "data": { 299 | "image/png": "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\n", 300 | "text/plain": [ 301 | "" 302 | ] 303 | }, 304 | "metadata": {}, 305 | "output_type": "display_data" 306 | } 307 | ], 308 | "source": [ 309 | "IMG_FILE = \"bird_project/bird.jpg\"\n", 310 | "FIG_SIZE = (6, 4)\n", 311 | "\n", 312 | "img = plt.imread(IMG_FILE)\n", 313 | "fig, ax = plt.subplots(figsize=FIG_SIZE)\n", 314 | "ax.imshow(img);" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": 20, 320 | "metadata": {}, 321 | "outputs": [ 322 | { 323 | "data": { 324 | "text/plain": [ 325 | "'http://localhost:8080/classificationbox/models/5b0ce5d8023d4e35/predict'" 326 | ] 327 | }, 328 | "execution_count": 20, 329 | "metadata": {}, 330 | "output_type": "execute_result" 331 | } 332 | ], 333 | "source": [ 334 | "MODEL_PREDICT_URL" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "## Predict via JSON encoded" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": 21, 347 | "metadata": {}, 348 | "outputs": [], 349 | "source": [ 350 | "predict_data = {\n", 351 | " \"inputs\": [\n", 352 | " {\"key\": \"image\", \"type\": \"image_base64\", \"value\": base64_encode_file(IMG_FILE)}]}\n", 353 | "\n", 354 | "try:\n", 355 | " response = requests.post(MODEL_PREDICT_URL, json=predict_data)\n", 356 | "except ValueError:\n", 357 | " print(\"Classificationbox error: {}\".format(response.json.text))\n", 358 | " # response = {}" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": 22, 364 | "metadata": {}, 365 | "outputs": [], 366 | "source": [ 367 | "# print(curlify.to_curl(response.request))" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 23, 373 | "metadata": {}, 374 | "outputs": [ 375 | { 376 | "data": { 377 | "text/plain": [ 378 | "{'success': True,\n", 379 | " 'classes': [{'id': 'birds', 'score': 0.915892},\n", 380 | " {'id': 'not_birds', 'score': 0.084108}]}" 381 | ] 382 | }, 383 | "execution_count": 23, 384 | "metadata": {}, 385 | "output_type": "execute_result" 386 | } 387 | ], 388 | "source": [ 389 | "requests_json = response.json()\n", 390 | "requests_json" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": 58, 396 | "metadata": {}, 397 | "outputs": [], 398 | "source": [ 399 | "def parse_classes(api_classes):\n", 400 | " \"\"\"Parse the API classes data into the format required.\"\"\"\n", 401 | " parsed_classes = []\n", 402 | " for entry in api_classes:\n", 403 | " class_ = {}\n", 404 | " class_[ATTR_ID] = entry['id']\n", 405 | " class_[ATTR_CONFIDENCE] = round(entry['score'] * 100.0, 2)\n", 406 | " parsed_classes.append(class_)\n", 407 | " return parsed_classes" 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "execution_count": 60, 413 | "metadata": {}, 414 | "outputs": [ 415 | { 416 | "data": { 417 | "text/plain": [ 418 | "[{'id': 'birds', 'confidence': 91.59}, {'id': 'not_birds', 'confidence': 8.41}]" 419 | ] 420 | }, 421 | "execution_count": 60, 422 | "metadata": {}, 423 | "output_type": "execute_result" 424 | } 425 | ], 426 | "source": [ 427 | "classes = parse_classes(requests_json['classes'])\n", 428 | "classes" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 62, 434 | "metadata": {}, 435 | "outputs": [ 436 | { 437 | "data": { 438 | "text/plain": [ 439 | "'birds'" 440 | ] 441 | }, 442 | "execution_count": 62, 443 | "metadata": {}, 444 | "output_type": "execute_result" 445 | } 446 | ], 447 | "source": [ 448 | "classes[0]['id']" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 17, 454 | "metadata": {}, 455 | "outputs": [], 456 | "source": [ 457 | "def get_classes(classes_json):\n", 458 | " \"\"\"Return the classes data.\"\"\"\n", 459 | " classes_dict = {class_result['id']: round(class_result['score'] * 100.0, 2)\n", 460 | " for class_result in classes_json}\n", 461 | " return classes_dict" 462 | ] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "execution_count": 20, 467 | "metadata": {}, 468 | "outputs": [ 469 | { 470 | "data": { 471 | "text/plain": [ 472 | "{'birds': 91.59, 'not_birds': 8.41}" 473 | ] 474 | }, 475 | "execution_count": 20, 476 | "metadata": {}, 477 | "output_type": "execute_result" 478 | } 479 | ], 480 | "source": [ 481 | "classes = get_classes(response.json()['classes'])\n", 482 | "classes" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": 21, 488 | "metadata": {}, 489 | "outputs": [ 490 | { 491 | "name": "stdout", 492 | "output_type": "stream", 493 | "text": [ 494 | "birds with confidence 91.59\n" 495 | ] 496 | } 497 | ], 498 | "source": [ 499 | "for key, value in classes.items():\n", 500 | " if value >= CONFIDENCE:\n", 501 | " print(\"{} with confidence {}\".format(key, value))" 502 | ] 503 | }, 504 | { 505 | "cell_type": "markdown", 506 | "metadata": {}, 507 | "source": [ 508 | "# Download model\n", 509 | "We can download the model as a binary file. A `model_{model_id}.classificationbox` data file will be downloaded" 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": null, 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [ 518 | "MODEL_STATE_URL # Pasting this in the browser will download the files" 519 | ] 520 | }, 521 | { 522 | "cell_type": "code", 523 | "execution_count": null, 524 | "metadata": {}, 525 | "outputs": [], 526 | "source": [ 527 | "%%time\n", 528 | "response = requests.get(MODEL_STATE_URL)" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": null, 534 | "metadata": {}, 535 | "outputs": [], 536 | "source": [ 537 | "filename = \"model_{}.classificationbox\".format(MODEL_ID)\n", 538 | "filename" 539 | ] 540 | }, 541 | { 542 | "cell_type": "code", 543 | "execution_count": null, 544 | "metadata": {}, 545 | "outputs": [], 546 | "source": [ 547 | "open(filename, 'wb').write(response.content)" 548 | ] 549 | }, 550 | { 551 | "cell_type": "code", 552 | "execution_count": null, 553 | "metadata": {}, 554 | "outputs": [], 555 | "source": [ 556 | "ls" 557 | ] 558 | }, 559 | { 560 | "cell_type": "markdown", 561 | "metadata": {}, 562 | "source": [ 563 | "# Upload model\n", 564 | "Now lets upload the model having restarted connection box" 565 | ] 566 | }, 567 | { 568 | "cell_type": "code", 569 | "execution_count": 30, 570 | "metadata": {}, 571 | "outputs": [ 572 | { 573 | "name": "stdout", 574 | "output_type": "stream", 575 | "text": [ 576 | "http://localhost:8080/classificationbox/state/5b0ce5d8023d4e35\n" 577 | ] 578 | }, 579 | { 580 | "data": { 581 | "text/plain": [ 582 | "{'success': True,\n", 583 | " 'id': '5b0ce5d8023d4e35',\n", 584 | " 'name': '5b0ce5d8023d4e35',\n", 585 | " 'options': {},\n", 586 | " 'predict_only': False}" 587 | ] 588 | }, 589 | "execution_count": 30, 590 | "metadata": {}, 591 | "output_type": "execute_result" 592 | } 593 | ], 594 | "source": [ 595 | "MODEL_ID = '5b0ce5d8023d4e35'\n", 596 | "MODEL_STATE_URL = 'http://{}:{}/classificationbox/state/{}'.format(IP, PORT, MODEL_ID)\n", 597 | "print(MODEL_STATE_URL)\n", 598 | "filename = '/Users/robincole/Documents/Data/Machinebox/classificationbox/birds.classificationbox'\n", 599 | "model_data = {\"base64\": base64_encode_file(filename)}\n", 600 | "\n", 601 | "requests.post(STATE_POST_URL, json=model_data, **kwargs).json()" 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "metadata": {}, 607 | "source": [ 608 | "## Class\n", 609 | "Make a class wrapper" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": null, 615 | "metadata": {}, 616 | "outputs": [], 617 | "source": [ 618 | "CLASSIFIER = 'classificationbox'\n", 619 | "\n", 620 | "class ClassificationboxEntity():\n", 621 | " \"\"\"Perform an image classification.\"\"\"\n", 622 | "\n", 623 | " def __init__(self, ip, port, camera_entity, model_id, model_name):\n", 624 | " \"\"\"Initialise a classificationbox model entity.\"\"\"\n", 625 | " self._base_url = \"http://{}:{}/{}/\".format(ip, port, CLASSIFIER)\n", 626 | " self._camera = camera_entity\n", 627 | "\n", 628 | " self._model_id = model_id\n", 629 | " self._model_name = model_name\n", 630 | "\n", 631 | " camera_name = camera_entity # split_entity_id(camera_entity)[1]\n", 632 | " self._name = \"{} {} {}\".format(\n", 633 | " CLASSIFIER, camera_name, model_name)\n", 634 | " \n", 635 | " @property\n", 636 | " def name(self):\n", 637 | " \"\"\"Return the name of the sensor.\"\"\"\n", 638 | " return self._name\n", 639 | " \n", 640 | " @property\n", 641 | " def device_state_attributes(self):\n", 642 | " \"\"\"Return the classifier attributes.\"\"\"\n", 643 | " return {\n", 644 | " 'model_id': self._model_id,\n", 645 | " 'model_name': self._model_name\n", 646 | " }\n", 647 | " " 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": {}, 654 | "outputs": [], 655 | "source": [ 656 | "entities = []\n", 657 | "\n", 658 | "if models_query['success']:\n", 659 | " for model in models_query['models']:\n", 660 | " camera_entity = 'mock_cam'\n", 661 | " entity = ClassificationboxEntity(IP, PORT, camera_entity, model['id'], model['name'])\n", 662 | " entities.append(entity)" 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "execution_count": null, 668 | "metadata": {}, 669 | "outputs": [], 670 | "source": [ 671 | "entities[1].name" 672 | ] 673 | }, 674 | { 675 | "cell_type": "code", 676 | "execution_count": null, 677 | "metadata": {}, 678 | "outputs": [], 679 | "source": [ 680 | "entities[1].device_state_attributes" 681 | ] 682 | }, 683 | { 684 | "cell_type": "code", 685 | "execution_count": null, 686 | "metadata": {}, 687 | "outputs": [], 688 | "source": [ 689 | "try:\n", 690 | " response = requests.get(MODELS_LIST_URL, timeout=9).json()\n", 691 | "except requests.exceptions.ConnectionError:\n", 692 | " _LOGGER.error(\"ConnectionError: Is %s running?\", CLASSIFIER)\n", 693 | "\n", 694 | "if response.staus_code == 200:\n", 695 | " print('success')" 696 | ] 697 | }, 698 | { 699 | "cell_type": "code", 700 | "execution_count": null, 701 | "metadata": {}, 702 | "outputs": [], 703 | "source": [ 704 | "requests.get(MODELS_LIST_URL, timeout=9).status_code" 705 | ] 706 | }, 707 | { 708 | "cell_type": "code", 709 | "execution_count": null, 710 | "metadata": {}, 711 | "outputs": [], 712 | "source": [ 713 | "MODELS_LIST_URL" 714 | ] 715 | }, 716 | { 717 | "cell_type": "code", 718 | "execution_count": null, 719 | "metadata": {}, 720 | "outputs": [], 721 | "source": [] 722 | } 723 | ], 724 | "metadata": { 725 | "kernelspec": { 726 | "display_name": "Python 3", 727 | "language": "python", 728 | "name": "python3" 729 | }, 730 | "language_info": { 731 | "codemirror_mode": { 732 | "name": "ipython", 733 | "version": 3 734 | }, 735 | "file_extension": ".py", 736 | "mimetype": "text/x-python", 737 | "name": "python", 738 | "nbconvert_exporter": "python", 739 | "pygments_lexer": "ipython3", 740 | "version": "3.6.5" 741 | } 742 | }, 743 | "nbformat": 4, 744 | "nbformat_minor": 2 745 | } 746 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Robin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Home-Assistant image classification using [Classificationbox](https://machinebox.io/docs/classificationbox). Follow [this guide](https://blog.machinebox.io/how-anyone-can-build-a-machine-learning-image-classifier-from-photos-on-your-hard-drive-very-5c20c6f2764f ) to create a model/models on Classificationbox. This component adds an `image_processing` entity for each model you have created on Classificationbox, where the state of the entity is the most likely classification of an image using that model. An `image_processing.image_classification` event is fired when the confidence in classification is above the threshold set by `confidence`, which defaults to 80%. 2 | 3 | Place the `custom_components` folder in your configuration directory (or add its contents to an existing custom_components folder). 4 | 5 | Add to your HA config: 6 | ```yaml 7 | image_processing: 8 | - platform: classificationbox 9 | ip_address: localhost 10 | port: 8080 11 | username: my_username 12 | password: my_password 13 | confidence: 50 14 | source: 15 | - entity_id: camera.local_file 16 | ``` 17 | 18 | Configuration variables: 19 | - **ip_address**: the ip of your Tagbox instance 20 | - **port**: the port of your Tagbox instance 21 | - **username**: (Optional) the username if you are using authentication 22 | - **password**: (Optional) the password if you are using authentication 23 | - **confidence** (Optional): The minimum of confidence in percent to fire an event. Defaults to 80 24 | - **source**: must be a camera. 25 | 26 | ## Automations using events 27 | 28 | Events can be used as a trigger for automations, and in the example automation below are used to trigger a notification with the image and the classification: 29 | 30 | ```yaml 31 | - action: 32 | - data_template: 33 | message: Class {{ trigger.event.data.id }} with probability {{ trigger.event.data.confidence 34 | }} 35 | title: New image classified 36 | data: 37 | file: ' {{states.camera.local_file.attributes.file_path}} ' 38 | service: notify.pushbullet 39 | alias: Send classification 40 | condition: [] 41 | id: '1120092824611' 42 | trigger: 43 | - event_data: 44 | id: birds 45 | event_type: image_processing.image_classification 46 | platform: event 47 | ``` 48 | 49 |

50 | 51 |

52 | 53 | ### Classificationbox 54 | Get/update Classificationbox [from Dockerhub](https://hub.docker.com/r/machinebox/classificationbox/) by running: 55 | ``` 56 | sudo docker pull machinebox/classificationbox 57 | ``` 58 | 59 | Run the container with: 60 | ``` 61 | MB_KEY="INSERT-YOUR-KEY-HERE" 62 | sudo docker run -p 8080:8080 -e "MB_KEY=$MB_KEY" machinebox/classificationbox 63 | ``` 64 | 65 | To run [with authentication](https://machinebox.io/docs/machine-box-apis#basic-authentication): 66 | ``` 67 | sudo docker run -e "MB_BASICAUTH_USER=my_username" -e "MB_BASICAUTH_PASS=my_password" -p 8080:8080 -e "MB_KEY=$MB_KEY" machinebox/classificationbox 68 | ``` 69 | 70 | #### Limiting computation 71 | [Image-classifier components](https://www.home-assistant.io/components/image_processing/) process the image from a camera at a fixed period given by the `scan_interval`. This leads to excessive computation if the image on the camera hasn't changed (for example if you are using a [local file camera](https://www.home-assistant.io/components/camera.local_file/) to display an image captured by a motion triggered system and this doesn't change often). The default `scan_interval` [is 10 seconds](https://github.com/home-assistant/home-assistant/blob/98e4d514a5130b747112cc0788fc2ef1d8e687c9/homeassistant/components/image_processing/__init__.py#L27). You can override this by adding to your config `scan_interval: 10000` (setting the interval to 10,000 seconds), and then call the `scan` [service](https://github.com/home-assistant/home-assistant/blob/98e4d514a5130b747112cc0788fc2ef1d8e687c9/homeassistant/components/image_processing/__init__.py#L62) when you actually want to process a camera image. So in my setup, I use an automation to call `scan` when a new motion triggered image has been saved and displayed on my local file camera. 72 | 73 | 74 | ## Local file camera 75 | Note that for development I am using a [file camera](https://www.home-assistant.io/components/camera.local_file/). 76 | ```yaml 77 | camera: 78 | - platform: local_file 79 | file_path: /images/bird.jpg 80 | ``` 81 | -------------------------------------------------------------------------------- /bird_project/5b0ce5d8023d4e35.classificationbox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/5b0ce5d8023d4e35.classificationbox -------------------------------------------------------------------------------- /bird_project/HA_motion_camera_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/HA_motion_camera_view.png -------------------------------------------------------------------------------- /bird_project/README.md: -------------------------------------------------------------------------------- 1 | ### Bird classification project 2 | **Summary of project:** automatically capture and classify images of birds visiting my bird-feeder. A long term goal of this project is to contribute the data to a nationwide study of bird populations. 3 | 4 | Being interested in bird watching, I attached a bird feeder to a window of my flat and within a few days various species of bird started visiting the feeder. I decided it would be fun to rig up a motion triggered camera to capture images of the birds, and I used Home-Assistant and a £10 USB webcam to capture images via motion trigger, and setup Home-Assistant to send an image notification to my phone when an image was captured. This setup is shown below: 5 | 6 |

7 | 8 |

9 | 10 | However I quickly discovered that all kinds of motion could trigger an image capture. The result was hundreds of images of all kinds of motion, such as planes flying in the distance or even funky light effects. Approximately less than half the images actually contained a bird, so I decided it was necessary to filter out the non-bird images. I have been interested in image classification for a while, and whilst searching online I came across [this article on Classificationbox](https://blog.machinebox.io/how-anyone-can-build-a-machine-learning-image-classifier-from-photos-on-your-hard-drive-very-5c20c6f2764f), which looked ideal for this project. 11 | 12 | This write-up will first present the image classification work using Classificationbox, then describe the practical implementation within an automated system. 13 | 14 | Tools used: 15 | * **Motion** software for capturing motion triggered images 16 | * **Classificationbox** deep learning classifier for classifying bird/not-bird images 17 | * **Home-Assistant** software for automated image capture and performing classification, recording data and sending notifications 18 | 19 | 20 | ### Introduction to Classificationbox 21 | [Classificationbox](https://machineboxio.com/docs/classificationbox) provides a ready-to-train deep-learning classifier, deployed in a Docker container and exposed via a REST API. It uses [online learning](https://en.wikipedia.org/wiki/Online_machine_learning) to train a classifier that can be used to automatically classify various types of data, such as text, images, structured and unstructured data. The publishers of Classificationbox are a company called [Machinebox](https://machineboxio.com/), based in London, UK. Their docs advise that the accuracy of a Classificationbox classifier improves with the number and quality of images supplied, where accuracy is the percentage of images correctly classified. If you have less than 30 images of each class (bird/not-bird in this case, so 60 images total), you may achieve better accuracy using their alternative product called [Tagbox](https://machineboxio.com/docs/tagbox), which uses [one-shot learning](https://en.wikipedia.org/wiki/One-shot_learning). I initially experimented with Tagbox but found that in many cases it could not identify a bird in the images since the illumination in the images is poor and often the bird appears as a colourless silhouette. After a few weeks of image captures I had over 1000 images of bird/not-bird so could proceed to use Classificationbox. 22 | 23 | Assuming you have [Docker installed](https://www.docker.com/community-edition), first download Classificationbox [from Dockerhub](https://hub.docker.com/r/machinebox/classificationbox/) by entering in the terminal: 24 | 25 | ``` 26 | sudo docker pull machinebox/classificationbox 27 | ``` 28 | 29 | Then run the Classificationbox container and expose it on port `8080` with: 30 | ``` 31 | MB_KEY="INSERT-YOUR-KEY-HERE" 32 | sudo docker run -p 8080:8080 -e "MB_KEY=$MB_KEY" machinebox/classificationbox 33 | ``` 34 | 35 | There are a number of ways you can interact with Classificationbox from the terminal, for example using [cURL](https://curl.haxx.se/), [HTTP](https://en.wikipedia.org/wiki/POST_(HTTP)) or python libraries such as [requests](http://docs.python-requests.org/en/master/). To check that Classificationbox is running correctly using cURL, and assuming you are on the same machine that Classificationbox is running on (`localhost`), at the terminal enter: 36 | 37 | ```curl 38 | curl http://localhost:8080/healthz 39 | ``` 40 | You should see a response similar to: 41 | 42 | ``` 43 | { 44 | "success": true, 45 | "hostname": "d6e51ge096c9", 46 | "metadata": { 47 | "boxname": "classificationbox", 48 | "build": "3ba550e" 49 | } 50 | ``` 51 | 52 | The if you don't get `"success": true` investigate the issue, otherwise you can proceed to training. 53 | 54 | ### Training Classificationbox 55 | [This article](https://blog.machinebox.io/how-anyone-can-build-a-machine-learning-image-classifier-from-photos-on-your-hard-drive-very-5c20c6f2764f) explains the training of Classificationbox, and links to a GO script which can be used to perform training. However if you have difficulty getting GO installed on your system (it took me a few tries!) I've also published a training script in python [teach_classificationbox.py](https://github.com/robmarkcole/classificationbox_python). One advantage of the GO script is that it will print out the accuracy of your model, which is a feature I will add to the python script in time. Whichever script you use, the first step is to decide on the classes you want to identify. For this project I wanted two classes, bird/not-bird images, with examples shown below. 56 | 57 |

58 | 59 |

60 | 61 | I had a total of over 1000 images that I manually sorted in two folders of bird/not-bird images, with each folder containing 500 images (this number may well be excessive, and in future work on this project I will experiment on reducing this number). Make sure that the images you use for training are representative of all the situations Classificationbox will encounter in use. For example, if you are capturing images at day and night, you want your teaching image set to also include images at day and night. With the images sorted into the two folders, I ran the GO script mentioned earlier and calculated that the model achieved 92% accuracy, pretty respectable! For 1000 images, teaching took about 30 minutes on my Macbook Pro with 8 GB RAM, but this will vary depending on you image set and hardware. 62 | 63 | Classificationbox is capable of hosting multiple models, and you will want to know the model ID of the model you just created. You can use cURL to check the ID: 64 | ```cURL 65 | curl http://localhost:8080/classificationbox/models 66 | ``` 67 | This should return something like: 68 | ``` 69 | { 70 | "success": true, 71 | "models": [ 72 | { 73 | "id": "5b0ce5d8023d4e35", 74 | "name": "5b0ce5d8023d4e35" 75 | } 76 | ] 77 | ``` 78 | Now that the model is created we can use another cURL command to perform a classification on an test image `bird.jpg`. I enter: 79 | ``` 80 | export FOO=`base64 -in /absolute/path/to/bird.jpg` 81 | 82 | curl -X POST -H "Content-Type: application/json" -d '{ "inputs": [ {"type": "image_base64", "key": "image", "value": "'$FOO'" } ] }' http://localhost:8080/classificationbox/models/5b0ce5d8023d4e35/predict 83 | ``` 84 | I then see: 85 | ``` 86 | { 87 | "success": true, 88 | "classes": [ 89 | { 90 | "id": "birds", 91 | "score": 0.915892 92 | }, 93 | { 94 | "id": "not_birds", 95 | "score": 0.084108 96 | } 97 | ] 98 | ``` 99 | 100 | Now that we have confirmed the model is performing correctly, we can download the model as a binary file. This is important if you are on the [free tier of Machinebox](https://machineboxio.com/#pricing) as the model will be erased every time you restart the Docker container. Once we have the model file we can upload it after restarting the Docker container, or transfer it another machine. In my case I performed teaching on my Macbook but actually want to use the model in production on a Synology NAS. To download the model file I used: 101 | 102 | ```cURL 103 | curl http://localhost:8080/classificationbox/state/5b0ce5d8023d4e35 --output 5b0ce5d8023d4e35.classificationbox 104 | ``` 105 | 106 | You will want to replace my model ID (`5b0ce5d8023d4e35`) with your own. Note that just heading to the URL above in your browser will also download the file. The downloaded file is 60 kb, so small enough to be shared on Github and elsewhere online. This is useful if you want others to be able to reproduce your work. 107 | 108 | To post the model file to Classificationbox use the cURL: 109 | ``` 110 | curl -X POST -F 'file=@/path/to/file/5b0ce5d8023d4e35.classificationbox' http://localhost:8080/classificationbox/state 111 | ``` 112 | 113 | You should see a response like: 114 | ``` 115 | {'success': True, 116 | 'id': '5b0ce5d8023d4e35', 117 | 'name': '5b0ce5d8023d4e35', 118 | 'options': {}, 119 | 'predict_only': False} 120 | ``` 121 | 122 | ### Using Classificationbox with Home-Assistant 123 | Home-Assistant is an open source, python 3 home automation hub, and if you are reading this article then I assume you are familiar with it. If not I refer you to the [documents online](https://www.home-assistant.io/). Note that there are a couple of different ways to run Home-Assistant. In this project I am using the Hassio approach which you should [read about here](https://www.home-assistant.io/hassio/), running on a Raspberry Pi 3, and a home-Assistant version newer than 0.70. However it doesn't matter how you have Home-Assistant running, this project should work with all common approaches. 124 | 125 | I have written code to use Classificationbox with Home-Assistant, and in this project we use this code with Home-Assistant to post images from my motion triggered USB camera to Classificationbox. If a bird image is classified, we are sent a mobile phone notification with the image. A diagram of the system is shown below: 126 | 127 |

128 | 129 |

130 | 131 | #### Hardware 132 | * **Webcam**: I picked up a [cheap webcam on Amazon](https://www.amazon.co.uk/gp/product/B000Q3VECE/ref=oh_aui_search_detailpage?ie=UTF8&psc=1). However you can use [any camera](https://www.home-assistant.io/components/#camera) that is compatible with Home-Assistant. 133 | * **Pi 3**: I have the camera connected via USB to a raspberry pi 3 running Home-Assistant. 134 | * **Synology NAS**: The Raspberry Pi 3 doesn't have sufficient RAM to run Classificationbox (2 GB min required) so instead I am running it on my [Synology DS216+II](https://www.amazon.co.uk/gp/product/B01G3HYR6G/ref=oh_aui_search_detailpage?ie=UTF8&psc=1) that I have [upgraded to have 8 GB RAM](http://blog.fedorov.com.au/2016/02/how-to-upgrade-memory-in-synology-ds216.html). Alternatively you could use a spare laptop, or host Classificationbox on a cloud service such as [Google Cloud](https://blog.machinebox.io/deploy-docker-containers-in-google-cloud-platform-4b921c77476b). 135 | * **Bird feeder**: My mum bought this, but there are similar online, just search for `window mounted birdfeeder`. 136 | 137 | #### Motion triggered image capture via Motion addon 138 | I connected the USB webcam to the Raspberry Pi running Home-Assistant and pointed the webcam at the birdfeeder. There are a number of options for viewing the camera feed in Home-Assistant, but since I am using Hassio and want motion detection, I decided to try out an approach which uses the [Motion](https://motion-project.github.io/) software deployed as a Hassio addon. [Hassio addons](https://www.home-assistant.io/addons/) are straightforward way to extend the functionality of Home-Assistant, and are installed via a page on the Home-Assistant interface, shown below: 139 | 140 |

141 | 142 |

143 | 144 | The addon I am using is written by [@HerrHofrat](https://github.com/HerrHofrat) and is called `Motion`, available at https://github.com/HerrHofrat/hassio-addons/tree/master/motion. You will need to add his repository as a location accessible to Hassio (search for the box that states `Add new repository by URL`). The addon will both continually capture still images, and capture timestamped images when motion is detected. I experimented with the addon settings but settled on the configuration below. The addon is configured by the Hassio tab for the addon: 145 | 146 | ```yaml 147 | { 148 | "config": "", 149 | "videodevice": "/dev/video0", 150 | "input": 0, 151 | "width": 640, 152 | "height": 480, 153 | "framerate": 2, 154 | "text_right": "%Y-%m-%d %T-%q", 155 | "target_dir": "/share/motion", 156 | "snapshot_interval": 1, 157 | "snapshot_name": "latest", 158 | "picture_output": "best", 159 | "picture_name": "%v-%Y_%m_%d_%H_%M_%S-motion-capture", 160 | "webcontrol_local": "on", 161 | "webcontrol_html": "on" 162 | } 163 | ``` 164 | 165 | The addon captures an image every second, saved as `latest.jpg`, and this image is continually over-written. On motion detection a timestamped image is captured with format `%v-%Y_%m_%d_%H_%M_%S-motion-capture.jpg`. All images are saved to the `/share/motion` folder on the Raspberry Pi. The above configuration should work regardless of the USB camera you are using, but if you have several USB cameras attached to your Pi you may need to use the terminal to check the camera interface (here `/dev/video0`). 166 | 167 | #### Displaying images on Home-Assistant 168 | I display the images captured by the addon using a pair of [local-file cameras](https://home-assistant.io/components/camera.local_file/). 169 | The continually updated `latest.jpg` is displayed on a camera with the name `Live view` and the most recent timestamped image captured will be displayed on a camera called `dummy`. The configuration for both cameras is added to `configuration.yaml`, shown below: 170 | 171 | ```yaml 172 | camera: 173 | - platform: local_file 174 | file_path: /share/motion/latest.jpg 175 | name: "Live view" 176 | - platform: local_file 177 | file_path: /share/motion/dummy.jpg 178 | name: "dummy" 179 | ``` 180 | **Note** that the image files (here `latest.jpg` and `dummy.jpg`) must be present when Home-Assistant starts as the component makes a check that the file exists, and therefore if running for the first time just copy some appropriately named images into the `/share/motion` folder. 181 | 182 | The final view of the camera feed in Home-Assistant is shown below. 183 | 184 |

185 | 186 |

187 | 188 | #### Classificationbox custom component 189 | To make Classificationbox accessible to Home-Assistant you will need to use the Classificationbox custom component code from this Github repo. This code is added to Home-Assistant by placing the contents of the `custom_components` folder in your Home-Assistant configuration directory (or adding its contents to an existing custom_components folder). The yaml code-blocks that follow are code to be entered in the Home-Assistant `configuration.yaml` file, unless otherwise stated. To configure the Classificationbox component, add to the Home-Assistant `configuration.yaml` file: 190 | 191 | ```yaml 192 | image_processing: 193 | - platform: classificationbox 194 | ip_address: localhost # Change to the IP hosting Classificationbox, e.g. 192.168.0.100 195 | port: 8080 196 | scan_interval: 100000 197 | source: 198 | - entity_id: camera.dummy 199 | ``` 200 | Note that by default the image will be classified every 10 seconds, but by setting a long `scan_interval` I am ensuring that image classification will only be performed when I trigger it using the `image_processing.scan` service described later. Note that the image source is `camera.dummy`, which will be the motion triggered image. The Classificationbox component fires an Home-Assistant `image_processing.image_classification` [event](https://www.home-assistant.io/docs/configuration/events/) when an image is classified with a probability greater than a threshold confidence of 80%, and we use this later to trigger a notification. 201 | 202 | #### Tying it all together 203 | Now that image capture is configured and Classificationbox is available to use, we must link them together using a sequence of automations in Home-Assistant. The sequence that we setup is illustrated in the diagram below, where the blue arrows represent automations: 204 | 205 |

206 | 207 |

208 | 209 | Out of the box, Home-Assistant has no knowledge of when the Hassio addon captures a new motion triggered image, so I use the [folder_watcher component](https://www.home-assistant.io/components/folder_watcher/) to alert Home-Assistant to new images in the `/share/motion` directory. The configuration of folder_watcher in `configuration.yaml` is: 210 | 211 | ```yaml 212 | folder_watcher: 213 | - folder: /share/motion 214 | patterns: 215 | - '*capture.jpg' 216 | ``` 217 | 218 | The `folder_watcher` component fires an event every time a new timestamped image is saved in `/share/motion` when the file name matches the pattern `*capture.jpg` (as the timestamped image file names do). The event data includes the file name and path to the added image, and I use an automation to display the new image on `camera.dummy` using the `camera.update_file_path` service. The configuration for the automation is shown below, added to `automations.yaml`: 219 | 220 | ```yaml 221 | - action: 222 | data_template: 223 | file_path: ' {{ trigger.event.data.path }} ' 224 | entity_id: camera.dummy 225 | service: camera.local_file_update_file_path 226 | alias: Display new image 227 | condition: [] 228 | id: '1520092824633' 229 | trigger: 230 | - event_data: 231 | event_type: created 232 | event_type: folder_watcher 233 | platform: event 234 | ``` 235 | 236 | I use a [template sensor](https://www.home-assistant.io/components/sensor.template/) to display the new image file path, which is available as an attribute on `camera.dummy`. The template sensor is configured in `sensors.yaml`: 237 | 238 | ```yaml 239 | - platform: template 240 | sensors: 241 | last_added_file: 242 | friendly_name: Last added file 243 | value_template: "{{states.camera.dummy.attributes.file_path}}" 244 | ``` 245 | 246 | I now use an automation to trigger the `image_processing.scan` service on `camera.dummy`. The `scan` service instructs Home-Assistant to send the image displayed by `camera.dummy` for classification by Classificationbox, and this automation is triggered by the state change of the `file_path` sensor. I add to `automations.yaml`: 247 | 248 | ```yaml 249 | - id: '1527837198169' 250 | alias: Perform image classification 251 | trigger: 252 | - entity_id: sensor.last_added_file 253 | platform: state 254 | condition: [] 255 | action: 256 | - data: 257 | entity_id: camera.dummy 258 | service: image_processing.scan 259 | ``` 260 | 261 | Finally I use the `image_processing.image_classification` event fired by the Classificationbox component to trigger an automation to send me any images of birds as a [Pushbullet](https://www.pushbullet.com/) notification: 262 | 263 | ```yaml 264 | - action: 265 | - data_template: 266 | message: Class {{ trigger.event.data.id }} with probability {{ trigger.event.data.confidence 267 | }} 268 | title: New image classified 269 | data: 270 | file: ' {{states.camera.local_file.attributes.file_path}} ' 271 | service: notify.pushbullet 272 | alias: Send classification 273 | condition: [] 274 | id: '1120092824611' 275 | trigger: 276 | - event_data: 277 | id: birds 278 | event_type: image_processing.image_classification 279 | platform: event 280 | ``` 281 | 282 | The notification is shown below. 283 | 284 |

285 | 286 |

287 | 288 | So finally we have achieved the aim of this project, and receive a notification when a bird image is captured by the motion triggered camera. 289 | 290 | ### Future Work 291 | Now that I have basic recognition of when a bird image is captured, the next step for this project is to train the classifier to recognise particular species of birds. I have a range of species visiting the birdfeeder including Blue Tits, Robins, Dunnocks, Magpis and even Parakeets. However the vast majority of the visitors are Blue Tits, and I have so far captured relatively few images of the other species, which will make training a classifier on these species more tricky. I may have to use photos of these species from the internet, but another idea is to create a website where users can submit their own images which are then used to create a 'community' classifier that can be shared amongst all users of the website. This would allow local studies such as this one to be reproduced at a large scale. I will also investigate ways to capture the statistics on visiting birds using [counters](https://www.home-assistant.io/components/counter/), and create a custom component to allow people to automatically submit their bird data to studies such as the RSPB annual [Birdwatch](https://www.rspb.org.uk/get-involved/activities/birdwatch/) survey. 292 | 293 | 294 | ### Summary 295 | In summary this article has described how to create an image classifier using Classificationbox, and how to deploy it for use within an automated system using Home-Assistant. A cheap USB webcam is used to capture motion triggered images, and these are posted to Classificationbox for classification. If there is a successful classification of a bird then the image is sent to my phone as a notification. Future work on this project is to train the classifier to identify different species of birds, and investigate ways to create a community classifier. One slight issue I have is that a Magpi has recently been trying to rip the feeder off the window (shown below), so I need to do some work to make it Magpi proof (Magpi destruction efforts shown below)! Additionally, now that it is Summer, this seasons crop of baby birds have fledged and my bird feeder has very few visitors as there is now abundant food in nature. However I will continue to refine the system and aim to deploy it next Spring. I hope this project inspires you to try out using deep-learning classifiers in your projects. 296 | 297 |

298 | 299 |

300 | **Above:** A Magpi either intentionally or unintentionally almost rips the bird feeder off the window. The arriving Blue Tit is unable to feed normally owing to the position of the feeder, but finds another way to feed via a hole in the top of the feeder! 301 | 302 | ### Links 303 | * Classificationbox: https://machineboxio.com/docs/classificationbox) 304 | * Classificationbox training blog post: https://blog.machinebox.io/how-anyone-can-build-a-machine-learning-image-classifier-from-photos-on-your-hard-drive-very-5c20c6f2764f 305 | * Home-Assistant: https://www.home-assistant.io/ 306 | * Hassio: https://www.home-assistant.io/hassio/ 307 | * Hassio Motion addon: https://github.com/HerrHofrat/hassio-addons/tree/master/motion 308 | * Docker: https://www.docker.com/community-edition 309 | * Classificationbox custom component for Home-Assistant: https://github.com/robmarkcole/HASS-Machinebox-Classificationbox 310 | * Pushbullet: https://www.pushbullet.com/ 311 | * RSPB Birdwatch: https://www.rspb.org.uk/get-involved/activities/birdwatch/ 312 | -------------------------------------------------------------------------------- /bird_project/bird.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/bird.jpg -------------------------------------------------------------------------------- /bird_project/bird_not_bird_examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/bird_not_bird_examples.png -------------------------------------------------------------------------------- /bird_project/bird_project.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/bird_project.pptx -------------------------------------------------------------------------------- /bird_project/hassio_addons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/hassio_addons.png -------------------------------------------------------------------------------- /bird_project/iphone_notification.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/iphone_notification.jpeg -------------------------------------------------------------------------------- /bird_project/magpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/magpi.png -------------------------------------------------------------------------------- /bird_project/not_bird.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/not_bird.jpg -------------------------------------------------------------------------------- /bird_project/sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/sequence.png -------------------------------------------------------------------------------- /bird_project/setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/setup.png -------------------------------------------------------------------------------- /bird_project/system_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/bird_project/system_overview.png -------------------------------------------------------------------------------- /custom_components/classificationbox/__init__.py: -------------------------------------------------------------------------------- 1 | """The classificationbox component.""" 2 | -------------------------------------------------------------------------------- /custom_components/classificationbox/image_processing.py: -------------------------------------------------------------------------------- 1 | """ 2 | Component that will perform classification of images via classiifcationbox. 3 | 4 | For more details about this platform, please refer to the documentation at 5 | https://home-assistant.io/components/image_processing.classificationbox 6 | """ 7 | import base64 8 | import logging 9 | from urllib.parse import urljoin 10 | 11 | import requests 12 | import voluptuous as vol 13 | 14 | from homeassistant.core import split_entity_id 15 | import homeassistant.helpers.config_validation as cv 16 | from homeassistant.components.image_processing import ( 17 | ATTR_CONFIDENCE, 18 | PLATFORM_SCHEMA, 19 | ImageProcessingEntity, 20 | CONF_SOURCE, 21 | CONF_ENTITY_ID, 22 | CONF_CONFIDENCE, 23 | ) 24 | from homeassistant.const import ( 25 | ATTR_ID, 26 | ATTR_ENTITY_ID, 27 | CONF_IP_ADDRESS, 28 | CONF_PORT, 29 | CONF_PASSWORD, 30 | CONF_USERNAME, 31 | ) 32 | 33 | from http import HTTPStatus #common HTTP Lib for statuses as per HA changes 34 | 35 | _LOGGER = logging.getLogger(__name__) 36 | 37 | ATTR_MODEL_ID = "model_id" 38 | ATTR_MODEL_NAME = "model_name" 39 | CLASSIFIER = "classificationbox" 40 | EVENT_IMAGE_CLASSIFICATION = "image_processing.image_classification" 41 | TIMEOUT = 9 42 | 43 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( 44 | { 45 | vol.Required(CONF_IP_ADDRESS): cv.string, 46 | vol.Required(CONF_PORT): cv.port, 47 | vol.Optional(CONF_USERNAME): cv.string, 48 | vol.Optional(CONF_PASSWORD): cv.string, 49 | } 50 | ) 51 | 52 | 53 | def check_box_health(url, username, password): 54 | """Check the health of the classifier and return its id if healthy.""" 55 | kwargs = {} 56 | if username: 57 | kwargs["auth"] = requests.auth.HTTPBasicAuth(username, password) 58 | try: 59 | response = requests.get(url, timeout=TIMEOUT, **kwargs) 60 | if response.status_code == HTTPStatus.UNAUTHORIZED: 61 | _LOGGER.error("AuthenticationError on %s", CLASSIFIER) 62 | return None 63 | if response.status_code == HTTPStatus.OK: 64 | return response.json()["hostname"] 65 | except requests.exceptions.ConnectionError: 66 | _LOGGER.error("ConnectionError: Is %s running?", CLASSIFIER) 67 | return None 68 | 69 | 70 | def encode_image(image): 71 | """base64 encode an image stream.""" 72 | base64_img = base64.b64encode(image).decode("ascii") 73 | return base64_img 74 | 75 | 76 | def get_matched_classes(classes): 77 | """Return the id and score of matched classes.""" 78 | return {class_[ATTR_ID]: class_[ATTR_CONFIDENCE] for class_ in classes} 79 | 80 | 81 | def get_models(url, username, password): 82 | """Return the list of models.""" 83 | kwargs = {} 84 | if username: 85 | kwargs["auth"] = requests.auth.HTTPBasicAuth(username, password) 86 | try: 87 | response = requests.get(url, timeout=TIMEOUT, **kwargs) 88 | response_json = response.json() 89 | if response_json["success"]: 90 | number_of_models = len(response_json["models"]) 91 | if number_of_models == 0: 92 | _LOGGER.error("%s error: No models found", CLASSIFIER) 93 | return None 94 | return response_json["models"] 95 | except requests.exceptions.ConnectionError: 96 | _LOGGER.error("ConnectionError: Is %s running?", CLASSIFIER) 97 | return None 98 | 99 | 100 | def parse_classes(api_classes): 101 | """Parse the API classes data into the format required, a list of dict.""" 102 | parsed_classes = [] 103 | for entry in api_classes: 104 | class_ = {} 105 | class_[ATTR_ID] = entry["id"] 106 | class_[ATTR_CONFIDENCE] = round(entry["score"] * 100.0, 2) 107 | parsed_classes.append(class_) 108 | return parsed_classes 109 | 110 | 111 | def post_image(url, image, username, password): 112 | """Post an image to the classifier.""" 113 | kwargs = {} 114 | if username: 115 | kwargs["auth"] = requests.auth.HTTPBasicAuth(username, password) 116 | input_json = { 117 | "inputs": [ 118 | {"key": "image", "type": "image_base64", "value": encode_image(image)} 119 | ] 120 | } 121 | try: 122 | response = requests.post(url, json=input_json, **kwargs) 123 | return response 124 | except requests.exceptions.ConnectionError: 125 | _LOGGER.error("ConnectionError: Is %s running?", CLASSIFIER) 126 | return None 127 | except ValueError: 128 | _LOGGER.error("Error with %s query", CLASSIFIER) 129 | return None 130 | 131 | 132 | def setup_platform(hass, config, add_devices, discovery_info=None): 133 | """Set up the classifier.""" 134 | entities = [] 135 | ip_address = config[CONF_IP_ADDRESS] 136 | port = config[CONF_PORT] 137 | username = config.get(CONF_USERNAME) 138 | password = config.get(CONF_PASSWORD) 139 | url_health = "http://{}:{}/healthz".format(ip_address, port) 140 | hostname = check_box_health(url_health, username, password) 141 | if hostname is None: 142 | return 143 | 144 | url_models = "http://{}:{}/{}/models".format(ip_address, port, CLASSIFIER) 145 | models = get_models(url_models, username, password) 146 | if models: 147 | for model in models: 148 | for camera in config[CONF_SOURCE]: 149 | entities.append( 150 | ClassificationboxEntity( 151 | ip_address, 152 | port, 153 | username, 154 | password, 155 | hostname, 156 | camera[CONF_ENTITY_ID], 157 | config[CONF_CONFIDENCE], 158 | model["id"], 159 | model["name"], 160 | ) 161 | ) 162 | add_devices(entities) 163 | 164 | 165 | class ClassificationboxEntity(ImageProcessingEntity): 166 | """Perform an image classification.""" 167 | 168 | def __init__( 169 | self, 170 | ip, 171 | port, 172 | username, 173 | password, 174 | hostname, 175 | camera_entity, 176 | confidence, 177 | model_id, 178 | model_name, 179 | ): 180 | """Init with the camera and model info.""" 181 | super().__init__() 182 | self._base_url = "http://{}:{}/{}/".format(ip, port, CLASSIFIER) 183 | self._username = username 184 | self._password = password 185 | self._hostname = hostname 186 | self._camera = camera_entity 187 | self._confidence = confidence 188 | self._model_id = model_id 189 | self._model_name = model_name 190 | camera_name = split_entity_id(camera_entity)[1] 191 | self._name = "{} {} {}".format(CLASSIFIER, camera_name, model_name) 192 | self._state = None 193 | self._matched = {} 194 | 195 | def process_image(self, image): 196 | """Process an image.""" 197 | predict_url = urljoin( 198 | self._base_url, "models/{}/predict".format(self._model_id) 199 | ) 200 | response = post_image(predict_url, image, self._username, self._password) 201 | if response is not None: 202 | response_json = response.json() 203 | if response_json["success"]: 204 | classes = parse_classes(response_json["classes"]) 205 | self._state = self.process_classes(classes) 206 | self._matched = get_matched_classes(classes) 207 | else: 208 | self._state = None 209 | self._matched = {} 210 | 211 | def process_classes(self, parsed_classes): 212 | """Send event for classes above threshold confidence.""" 213 | state = None 214 | for class_ in parsed_classes: 215 | if class_[ATTR_CONFIDENCE] >= self._confidence: 216 | self.hass.bus.fire( 217 | EVENT_IMAGE_CLASSIFICATION, 218 | { 219 | "classifier": CLASSIFIER, 220 | ATTR_ENTITY_ID: self.entity_id, 221 | ATTR_MODEL_ID: self._model_id, 222 | ATTR_MODEL_NAME: self._model_name, 223 | ATTR_ID: class_[ATTR_ID], 224 | ATTR_CONFIDENCE: class_[ATTR_CONFIDENCE], 225 | }, 226 | ) 227 | if parsed_classes[0][ATTR_CONFIDENCE] >= self._confidence: 228 | state = parsed_classes[0][ATTR_ID] 229 | return state 230 | 231 | @property 232 | def camera_entity(self): 233 | """Return camera entity id from process pictures.""" 234 | return self._camera 235 | 236 | @property 237 | def name(self): 238 | """Return the name of the sensor.""" 239 | return self._name 240 | 241 | @property 242 | def state(self): 243 | """Return the state of the entity.""" 244 | return self._state 245 | 246 | @property 247 | def device_state_attributes(self): 248 | """Return the classifier attributes.""" 249 | attr = { 250 | ATTR_CONFIDENCE: self._confidence, 251 | ATTR_MODEL_ID: self._model_id, 252 | ATTR_MODEL_NAME: self._model_name, 253 | "hostname": self._hostname, 254 | } 255 | attr.update(self._matched) 256 | return attr 257 | -------------------------------------------------------------------------------- /custom_components/classificationbox/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "classificationbox", 3 | "name": "classificationbox custom component", 4 | "documentation": "https://github.com/robmarkcole/HASS-Machinebox-Classificationbox", 5 | "requirements": [ 6 | "requests" 7 | ], 8 | "dependencies": [], 9 | "codeowners": [ 10 | "@robmarkcole" 11 | ], 12 | "version": "1.0.0" 13 | } -------------------------------------------------------------------------------- /development.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Have trained a classifier following https://blog.machinebox.io/how-anyone-can-build-a-machine-learning-image-classifier-from-photos-on-your-hard-drive-very-5c20c6f2764f using my own bird data. Achieved an accuracy of 88% - but the first time I ran the script (different random selection of images) accuracy was about 92%, so clearly I am very sensitive to the images randomly selected and should add more images:\n", 8 | "```\n", 9 | "Correct: 180\n", 10 | "Incorrect: 23\n", 11 | "Errors: 0\n", 12 | "Accuracy: 88.66995073891626%\n", 13 | "```\n", 14 | "\n", 15 | "https://machineboxio.com/docs/classificationbox\n", 16 | "\n", 17 | "classificationbox is online learning (supervised learning), it works also with little data but it build the classifier function so it needs more data. for my bird monirtoring project my 2 classes would be bird/no_bird, and I just post the image data\n", 18 | "\n", 19 | "Classifiers can be made to help solve a wide range of example use cases, for example:\n", 20 | "\n", 21 | "* Learn about how your company is perceived by grouping tweets into positive and negative\n", 22 | "* Automatically group photos of cats and dogs\n", 23 | "* Group emails into spam and non-spam categories\n", 24 | "* Build a classifier to detect the language of a piece of text based on previously taught examples\n", 25 | "\n", 26 | "```\n", 27 | "sudo docker pull machinebox/classificationbox\n", 28 | "\n", 29 | "sudo docker run -p 8080:8080 -e \"MB_KEY=$MB_KEY\" machinebox/classificationbox\n", 30 | "```\n", 31 | "http://localhost:8080/\n", 32 | "\n", 33 | "Note that if you restart classificationbox models will be lost" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 1, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "import requests\n", 43 | "# import curlify\n", 44 | "import re\n", 45 | "import json\n", 46 | "import base64\n", 47 | "import matplotlib.pyplot as plt\n", 48 | "import matplotlib.patches as patches\n", 49 | "%matplotlib inline\n", 50 | "\n", 51 | "def base64_encode_file(file_path):\n", 52 | " \"\"\"\n", 53 | " Takes the path to an image and returns the base64 encoded\n", 54 | " image data as a string.\n", 55 | " \"\"\"\n", 56 | " with open(file_path, \"rb\") as f:\n", 57 | " file_data = base64.b64encode(f.read()).decode('ascii')\n", 58 | " return file_data\n", 59 | "\n", 60 | "ATTR_ID = 'id'\n", 61 | "ATTR_CONFIDENCE = 'confidence'" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 25, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "IP = 'localhost'\n", 71 | "#IP = '192.168.0.30'\n", 72 | "PORT = '8080'\n", 73 | "CLASSIFIER = 'classificationbox'\n", 74 | "CONFIDENCE = 80\n", 75 | "\n", 76 | "MODELS_URL = 'http://{}:{}/{}/models'.format(IP, PORT, CLASSIFIER)\n", 77 | "MODEL_CREATION_URL = 'http://{}:{}/{}/models'.format(IP, PORT, CLASSIFIER)\n", 78 | "STATE_POST_URL = 'http://{}:{}/classificationbox/state'.format(IP, PORT)\n", 79 | "\n", 80 | "username = 'my_username'\n", 81 | "password = 'my_password'\n", 82 | "kwargs = {}\n", 83 | "if username:\n", 84 | " kwargs['auth'] = requests.auth.HTTPBasicAuth(username, password)" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 26, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "text/plain": [ 95 | "'http://localhost:8080/classificationbox/models'" 96 | ] 97 | }, 98 | "execution_count": 26, 99 | "metadata": {}, 100 | "output_type": "execute_result" 101 | } 102 | ], 103 | "source": [ 104 | "MODELS_URL" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 27, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "def get_models(url):\n", 114 | " \"\"\"Return the list of models.\"\"\"\n", 115 | " try:\n", 116 | " response = requests.get(url, **kwargs)\n", 117 | " response_json = response.json()\n", 118 | " if response_json['success']:\n", 119 | " if len(response_json['models']) == 0:\n", 120 | " print(\"%s error: No models found\", CLASSIFIER)\n", 121 | " else:\n", 122 | " return response_json['models']\n", 123 | " except requests.exceptions.ConnectionError:\n", 124 | " print(\"ConnectionError: Is %s running?\", CLASSIFIER)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 28, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "data": { 134 | "text/plain": [ 135 | "{'auth': }" 136 | ] 137 | }, 138 | "execution_count": 28, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "kwargs" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 31, 150 | "metadata": {}, 151 | "outputs": [ 152 | { 153 | "data": { 154 | "text/plain": [ 155 | "{'success': True,\n", 156 | " 'models': [{'id': '5b0ce5d8023d4e35', 'name': '5b0ce5d8023d4e35'}]}" 157 | ] 158 | }, 159 | "execution_count": 31, 160 | "metadata": {}, 161 | "output_type": "execute_result" 162 | } 163 | ], 164 | "source": [ 165 | "response = requests.get(MODELS_URL, timeout=9, **kwargs)\n", 166 | "response_json = response.json()\n", 167 | "response_json" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "Lets list my models" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 6, 180 | "metadata": {}, 181 | "outputs": [ 182 | { 183 | "data": { 184 | "text/plain": [ 185 | "[{'id': '5b0ce5d8023d4e35', 'name': '5b0ce5d8023d4e35'}]" 186 | ] 187 | }, 188 | "execution_count": 6, 189 | "metadata": {}, 190 | "output_type": "execute_result" 191 | } 192 | ], 193 | "source": [ 194 | "models = get_models(MODELS_URL)\n", 195 | "models" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 7, 201 | "metadata": {}, 202 | "outputs": [ 203 | { 204 | "name": "stdout", 205 | "output_type": "stream", 206 | "text": [ 207 | "5b0ce5d8023d4e35 5b0ce5d8023d4e35\n" 208 | ] 209 | } 210 | ], 211 | "source": [ 212 | "for model in models:\n", 213 | " print(model['id'], model['name'])" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": {}, 219 | "source": [ 220 | "I want the model which was generated by the GO script on my bird data" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 16, 226 | "metadata": {}, 227 | "outputs": [ 228 | { 229 | "data": { 230 | "text/plain": [ 231 | "'5b0ce5d8023d4e35'" 232 | ] 233 | }, 234 | "execution_count": 16, 235 | "metadata": {}, 236 | "output_type": "execute_result" 237 | } 238 | ], 239 | "source": [ 240 | "MODEL_ID = models[0]['id']\n", 241 | "MODEL_ID" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": 17, 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "MODEL_PREDICT_URL = 'http://{}:{}/{}/models/{}/predict'.format(IP, PORT, CLASSIFIER, MODEL_ID)\n", 251 | "MODEL_STATS_URL = 'http://{}:{}/{}/models/{}/stats'.format(IP, PORT, CLASSIFIER, MODEL_ID)\n", 252 | "\n", 253 | "MODEL_STATE_URL = 'http://{}:{}/classificationbox/state/{}'.format(IP, PORT, MODEL_ID)\n", 254 | "MODEL_TEACH_URL = 'http://{}:{}/{}/models/{}/teach'.format(IP, PORT, CLASSIFIER, MODEL_ID)" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "Now lets see the stats on this model" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 18, 267 | "metadata": {}, 268 | "outputs": [ 269 | { 270 | "data": { 271 | "text/plain": [ 272 | "{'success': True, 'predictions': 1, 'examples': 0, 'classes': []}" 273 | ] 274 | }, 275 | "execution_count": 18, 276 | "metadata": {}, 277 | "output_type": "execute_result" 278 | } 279 | ], 280 | "source": [ 281 | "model_stats = requests.get(MODEL_STATS_URL).json()\n", 282 | "model_stats" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "Lets make a prediction on a bird image" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": 19, 295 | "metadata": {}, 296 | "outputs": [ 297 | { 298 | "data": { 299 | "image/png": "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\n", 300 | "text/plain": [ 301 | "" 302 | ] 303 | }, 304 | "metadata": {}, 305 | "output_type": "display_data" 306 | } 307 | ], 308 | "source": [ 309 | "IMG_FILE = \"bird_project/bird.jpg\"\n", 310 | "FIG_SIZE = (6, 4)\n", 311 | "\n", 312 | "img = plt.imread(IMG_FILE)\n", 313 | "fig, ax = plt.subplots(figsize=FIG_SIZE)\n", 314 | "ax.imshow(img);" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": 20, 320 | "metadata": {}, 321 | "outputs": [ 322 | { 323 | "data": { 324 | "text/plain": [ 325 | "'http://localhost:8080/classificationbox/models/5b0ce5d8023d4e35/predict'" 326 | ] 327 | }, 328 | "execution_count": 20, 329 | "metadata": {}, 330 | "output_type": "execute_result" 331 | } 332 | ], 333 | "source": [ 334 | "MODEL_PREDICT_URL" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "## Predict via JSON encoded" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": 21, 347 | "metadata": {}, 348 | "outputs": [], 349 | "source": [ 350 | "predict_data = {\n", 351 | " \"inputs\": [\n", 352 | " {\"key\": \"image\", \"type\": \"image_base64\", \"value\": base64_encode_file(IMG_FILE)}]}\n", 353 | "\n", 354 | "try:\n", 355 | " response = requests.post(MODEL_PREDICT_URL, json=predict_data)\n", 356 | "except ValueError:\n", 357 | " print(\"Classificationbox error: {}\".format(response.json.text))\n", 358 | " # response = {}" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": 22, 364 | "metadata": {}, 365 | "outputs": [], 366 | "source": [ 367 | "# print(curlify.to_curl(response.request))" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 23, 373 | "metadata": {}, 374 | "outputs": [ 375 | { 376 | "data": { 377 | "text/plain": [ 378 | "{'success': True,\n", 379 | " 'classes': [{'id': 'birds', 'score': 0.915892},\n", 380 | " {'id': 'not_birds', 'score': 0.084108}]}" 381 | ] 382 | }, 383 | "execution_count": 23, 384 | "metadata": {}, 385 | "output_type": "execute_result" 386 | } 387 | ], 388 | "source": [ 389 | "requests_json = response.json()\n", 390 | "requests_json" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": 58, 396 | "metadata": {}, 397 | "outputs": [], 398 | "source": [ 399 | "def parse_classes(api_classes):\n", 400 | " \"\"\"Parse the API classes data into the format required.\"\"\"\n", 401 | " parsed_classes = []\n", 402 | " for entry in api_classes:\n", 403 | " class_ = {}\n", 404 | " class_[ATTR_ID] = entry['id']\n", 405 | " class_[ATTR_CONFIDENCE] = round(entry['score'] * 100.0, 2)\n", 406 | " parsed_classes.append(class_)\n", 407 | " return parsed_classes" 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "execution_count": 60, 413 | "metadata": {}, 414 | "outputs": [ 415 | { 416 | "data": { 417 | "text/plain": [ 418 | "[{'id': 'birds', 'confidence': 91.59}, {'id': 'not_birds', 'confidence': 8.41}]" 419 | ] 420 | }, 421 | "execution_count": 60, 422 | "metadata": {}, 423 | "output_type": "execute_result" 424 | } 425 | ], 426 | "source": [ 427 | "classes = parse_classes(requests_json['classes'])\n", 428 | "classes" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 62, 434 | "metadata": {}, 435 | "outputs": [ 436 | { 437 | "data": { 438 | "text/plain": [ 439 | "'birds'" 440 | ] 441 | }, 442 | "execution_count": 62, 443 | "metadata": {}, 444 | "output_type": "execute_result" 445 | } 446 | ], 447 | "source": [ 448 | "classes[0]['id']" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 17, 454 | "metadata": {}, 455 | "outputs": [], 456 | "source": [ 457 | "def get_classes(classes_json):\n", 458 | " \"\"\"Return the classes data.\"\"\"\n", 459 | " classes_dict = {class_result['id']: round(class_result['score'] * 100.0, 2)\n", 460 | " for class_result in classes_json}\n", 461 | " return classes_dict" 462 | ] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "execution_count": 20, 467 | "metadata": {}, 468 | "outputs": [ 469 | { 470 | "data": { 471 | "text/plain": [ 472 | "{'birds': 91.59, 'not_birds': 8.41}" 473 | ] 474 | }, 475 | "execution_count": 20, 476 | "metadata": {}, 477 | "output_type": "execute_result" 478 | } 479 | ], 480 | "source": [ 481 | "classes = get_classes(response.json()['classes'])\n", 482 | "classes" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": 21, 488 | "metadata": {}, 489 | "outputs": [ 490 | { 491 | "name": "stdout", 492 | "output_type": "stream", 493 | "text": [ 494 | "birds with confidence 91.59\n" 495 | ] 496 | } 497 | ], 498 | "source": [ 499 | "for key, value in classes.items():\n", 500 | " if value >= CONFIDENCE:\n", 501 | " print(\"{} with confidence {}\".format(key, value))" 502 | ] 503 | }, 504 | { 505 | "cell_type": "markdown", 506 | "metadata": {}, 507 | "source": [ 508 | "# Download model\n", 509 | "We can download the model as a binary file. A `model_{model_id}.classificationbox` data file will be downloaded" 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": null, 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [ 518 | "MODEL_STATE_URL # Pasting this in the browser will download the files" 519 | ] 520 | }, 521 | { 522 | "cell_type": "code", 523 | "execution_count": null, 524 | "metadata": {}, 525 | "outputs": [], 526 | "source": [ 527 | "%%time\n", 528 | "response = requests.get(MODEL_STATE_URL)" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": null, 534 | "metadata": {}, 535 | "outputs": [], 536 | "source": [ 537 | "filename = \"model_{}.classificationbox\".format(MODEL_ID)\n", 538 | "filename" 539 | ] 540 | }, 541 | { 542 | "cell_type": "code", 543 | "execution_count": null, 544 | "metadata": {}, 545 | "outputs": [], 546 | "source": [ 547 | "open(filename, 'wb').write(response.content)" 548 | ] 549 | }, 550 | { 551 | "cell_type": "code", 552 | "execution_count": null, 553 | "metadata": {}, 554 | "outputs": [], 555 | "source": [ 556 | "ls" 557 | ] 558 | }, 559 | { 560 | "cell_type": "markdown", 561 | "metadata": {}, 562 | "source": [ 563 | "# Upload model\n", 564 | "Now lets upload the model having restarted connection box" 565 | ] 566 | }, 567 | { 568 | "cell_type": "code", 569 | "execution_count": 30, 570 | "metadata": {}, 571 | "outputs": [ 572 | { 573 | "name": "stdout", 574 | "output_type": "stream", 575 | "text": [ 576 | "http://localhost:8080/classificationbox/state/5b0ce5d8023d4e35\n" 577 | ] 578 | }, 579 | { 580 | "data": { 581 | "text/plain": [ 582 | "{'success': True,\n", 583 | " 'id': '5b0ce5d8023d4e35',\n", 584 | " 'name': '5b0ce5d8023d4e35',\n", 585 | " 'options': {},\n", 586 | " 'predict_only': False}" 587 | ] 588 | }, 589 | "execution_count": 30, 590 | "metadata": {}, 591 | "output_type": "execute_result" 592 | } 593 | ], 594 | "source": [ 595 | "MODEL_ID = '5b0ce5d8023d4e35'\n", 596 | "MODEL_STATE_URL = 'http://{}:{}/classificationbox/state/{}'.format(IP, PORT, MODEL_ID)\n", 597 | "print(MODEL_STATE_URL)\n", 598 | "filename = '/Users/robincole/Documents/Data/Machinebox/classificationbox/birds.classificationbox'\n", 599 | "model_data = {\"base64\": base64_encode_file(filename)}\n", 600 | "\n", 601 | "requests.post(STATE_POST_URL, json=model_data, **kwargs).json()" 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "metadata": {}, 607 | "source": [ 608 | "## Class\n", 609 | "Make a class wrapper" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": null, 615 | "metadata": {}, 616 | "outputs": [], 617 | "source": [ 618 | "CLASSIFIER = 'classificationbox'\n", 619 | "\n", 620 | "class ClassificationboxEntity():\n", 621 | " \"\"\"Perform an image classification.\"\"\"\n", 622 | "\n", 623 | " def __init__(self, ip, port, camera_entity, model_id, model_name):\n", 624 | " \"\"\"Initialise a classificationbox model entity.\"\"\"\n", 625 | " self._base_url = \"http://{}:{}/{}/\".format(ip, port, CLASSIFIER)\n", 626 | " self._camera = camera_entity\n", 627 | "\n", 628 | " self._model_id = model_id\n", 629 | " self._model_name = model_name\n", 630 | "\n", 631 | " camera_name = camera_entity # split_entity_id(camera_entity)[1]\n", 632 | " self._name = \"{} {} {}\".format(\n", 633 | " CLASSIFIER, camera_name, model_name)\n", 634 | " \n", 635 | " @property\n", 636 | " def name(self):\n", 637 | " \"\"\"Return the name of the sensor.\"\"\"\n", 638 | " return self._name\n", 639 | " \n", 640 | " @property\n", 641 | " def device_state_attributes(self):\n", 642 | " \"\"\"Return the classifier attributes.\"\"\"\n", 643 | " return {\n", 644 | " 'model_id': self._model_id,\n", 645 | " 'model_name': self._model_name\n", 646 | " }\n", 647 | " " 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": {}, 654 | "outputs": [], 655 | "source": [ 656 | "entities = []\n", 657 | "\n", 658 | "if models_query['success']:\n", 659 | " for model in models_query['models']:\n", 660 | " camera_entity = 'mock_cam'\n", 661 | " entity = ClassificationboxEntity(IP, PORT, camera_entity, model['id'], model['name'])\n", 662 | " entities.append(entity)" 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "execution_count": null, 668 | "metadata": {}, 669 | "outputs": [], 670 | "source": [ 671 | "entities[1].name" 672 | ] 673 | }, 674 | { 675 | "cell_type": "code", 676 | "execution_count": null, 677 | "metadata": {}, 678 | "outputs": [], 679 | "source": [ 680 | "entities[1].device_state_attributes" 681 | ] 682 | }, 683 | { 684 | "cell_type": "code", 685 | "execution_count": null, 686 | "metadata": {}, 687 | "outputs": [], 688 | "source": [ 689 | "try:\n", 690 | " response = requests.get(MODELS_LIST_URL, timeout=9).json()\n", 691 | "except requests.exceptions.ConnectionError:\n", 692 | " _LOGGER.error(\"ConnectionError: Is %s running?\", CLASSIFIER)\n", 693 | "\n", 694 | "if response.staus_code == 200:\n", 695 | " print('success')" 696 | ] 697 | }, 698 | { 699 | "cell_type": "code", 700 | "execution_count": null, 701 | "metadata": {}, 702 | "outputs": [], 703 | "source": [ 704 | "requests.get(MODELS_LIST_URL, timeout=9).status_code" 705 | ] 706 | }, 707 | { 708 | "cell_type": "code", 709 | "execution_count": null, 710 | "metadata": {}, 711 | "outputs": [], 712 | "source": [ 713 | "MODELS_LIST_URL" 714 | ] 715 | }, 716 | { 717 | "cell_type": "code", 718 | "execution_count": null, 719 | "metadata": {}, 720 | "outputs": [], 721 | "source": [] 722 | }, 723 | { 724 | "cell_type": "code", 725 | "execution_count": null, 726 | "metadata": {}, 727 | "outputs": [], 728 | "source": [] 729 | } 730 | ], 731 | "metadata": { 732 | "kernelspec": { 733 | "display_name": "Python 3", 734 | "language": "python", 735 | "name": "python3" 736 | }, 737 | "language_info": { 738 | "codemirror_mode": { 739 | "name": "ipython", 740 | "version": 3 741 | }, 742 | "file_extension": ".py", 743 | "mimetype": "text/x-python", 744 | "name": "python", 745 | "nbconvert_exporter": "python", 746 | "pygments_lexer": "ipython3", 747 | "version": "3.6.5" 748 | } 749 | }, 750 | "nbformat": 4, 751 | "nbformat_minor": 2 752 | } 753 | -------------------------------------------------------------------------------- /tests/test_classificationbox.py: -------------------------------------------------------------------------------- 1 | """The tests for the classificationbox component.""" 2 | from unittest.mock import patch 3 | 4 | import pytest 5 | import requests 6 | import requests_mock 7 | 8 | from homeassistant.core import callback 9 | from homeassistant.const import ( 10 | ATTR_ID, ATTR_ENTITY_ID, CONF_FRIENDLY_NAME, CONF_PASSWORD, 11 | CONF_USERNAME, CONF_IP_ADDRESS, CONF_PORT, HTTP_OK, 12 | HTTP_UNAUTHORIZED) 13 | from homeassistant.setup import async_setup_component 14 | import homeassistant.components.image_processing as ip 15 | import homeassistant.components.image_processing.classificationbox as cb 16 | 17 | MOCK_BOX_ID = 'b893cc4f7fd6' 18 | MOCK_IP = '192.168.0.1' 19 | MOCK_PORT = '8080' 20 | 21 | MOCK_FILE_PATH = '/images/mock.jpg' 22 | 23 | MOCK_HEALTH = {'success': True, 24 | 'hostname': 'b893cc4f7fd6', 25 | 'metadata': {'boxname': 'classificationbox', 26 | 'build': 'development'}, 27 | 'errors': []} 28 | 29 | MOCK_JSON = {'success': True, 30 | 'classes': [{'id': 'birds', 'score': 0.915892}, 31 | {'id': 'not_birds', 'score': 0.084108}]} 32 | 33 | MOCK_NO_MODELS = {'success': True, 'models': []} 34 | MOCK_MODEL = [{'id': '12345', 'name': '12345'}] 35 | MOCK_WITH_MODEL = {'success': True, 'models': MOCK_MODEL} 36 | MOCK_MODEL_ID = '12345' 37 | MOCK_NAME = 'mock_name' 38 | MOCK_USERNAME = 'mock_username' 39 | MOCK_PASSWORD = 'mock_password' 40 | 41 | # Classes data after parsing. 42 | PARSED_CLASSES = [{ATTR_ID: 'birds', ip.ATTR_CONFIDENCE: 91.59}, 43 | {ATTR_ID: 'not_birds', ip.ATTR_CONFIDENCE: 8.41}] 44 | 45 | MATCHED_CLASSES = {'birds': 91.59, 'not_birds': 8.41} 46 | 47 | VALID_ENTITY_ID = 'image_processing.classificationbox_demo_camera_12345' 48 | VALID_CONFIG = { 49 | ip.DOMAIN: { 50 | 'platform': 'classificationbox', 51 | CONF_IP_ADDRESS: MOCK_IP, 52 | CONF_PORT: MOCK_PORT, 53 | ip.CONF_SOURCE: { 54 | ip.CONF_ENTITY_ID: 'camera.demo_camera'} 55 | }, 56 | 'camera': { 57 | 'platform': 'demo' 58 | } 59 | } 60 | 61 | 62 | @pytest.fixture 63 | def mock_healthybox(): 64 | """Mock cb.check_box_health.""" 65 | check_box_health = 'homeassistant.components.image_processing.' \ 66 | 'classificationbox.check_box_health' 67 | with patch(check_box_health, return_value=MOCK_BOX_ID) as _mock_healthybox: 68 | yield _mock_healthybox 69 | 70 | 71 | @pytest.fixture 72 | def mock_image(): 73 | """Return a mock camera image.""" 74 | with patch('homeassistant.components.camera.demo.DemoCamera.camera_image', 75 | return_value=b'Test') as image: 76 | yield image 77 | 78 | 79 | def test_check_box_health(caplog): 80 | """Test check box health.""" 81 | with requests_mock.Mocker() as mock_req: 82 | url = "http://{}:{}/healthz".format(MOCK_IP, MOCK_PORT) 83 | mock_req.get(url, status_code=HTTP_OK, json=MOCK_HEALTH) 84 | assert cb.check_box_health(url, 'user', 'pass') == MOCK_BOX_ID 85 | 86 | mock_req.get(url, status_code=HTTP_UNAUTHORIZED) 87 | assert cb.check_box_health(url, None, None) is None 88 | assert "AuthenticationError on classificationbox" in caplog.text 89 | 90 | mock_req.get(url, exc=requests.exceptions.ConnectTimeout) 91 | cb.check_box_health(url, None, None) 92 | assert "ConnectionError: Is classificationbox running?" in caplog.text 93 | 94 | 95 | def test_encode_image(): 96 | """Test that binary data is encoded correctly.""" 97 | assert cb.encode_image(b'test') == 'dGVzdA==' 98 | 99 | 100 | def test_get_matched_classes(): 101 | """Test that matched_classes are parsed correctly.""" 102 | assert cb.get_matched_classes(PARSED_CLASSES) == MATCHED_CLASSES 103 | 104 | 105 | def test_get_models(caplog): 106 | """Test querying the list of models.""" 107 | with requests_mock.Mocker() as mock_req: 108 | url = "http://{}:{}/{}/models".format( 109 | MOCK_IP, MOCK_PORT, cb.CLASSIFIER) 110 | mock_req.get(url, json=MOCK_NO_MODELS) 111 | assert cb.get_models(url, 'user', 'pass') is None 112 | assert "classificationbox error: No models found" in caplog.text 113 | 114 | mock_req.get(url, json=MOCK_WITH_MODEL) 115 | assert cb.get_models(url, 'user', 'pass') == MOCK_MODEL 116 | 117 | mock_req.get(url, exc=requests.exceptions.ConnectTimeout) 118 | cb.get_models(url, 'user', 'pass') 119 | assert "ConnectionError: Is classificationbox running?" in caplog.text 120 | 121 | 122 | def test_parse_classes(): 123 | """Test parsing of raw API data.""" 124 | assert cb.parse_classes(MOCK_JSON['classes']) == PARSED_CLASSES 125 | 126 | 127 | async def test_setup_platform(hass, mock_healthybox): 128 | """Setup platform with one entity.""" 129 | with patch('homeassistant.components.image_processing.' 130 | 'classificationbox.get_models', 131 | return_value=MOCK_MODEL): 132 | await async_setup_component(hass, ip.DOMAIN, VALID_CONFIG) 133 | await hass.async_block_till_done() 134 | assert hass.states.get(VALID_ENTITY_ID) 135 | 136 | 137 | async def test_setup_platform_with_auth(hass, mock_healthybox): 138 | """Setup platform with one entity and auth.""" 139 | valid_config_auth = VALID_CONFIG.copy() 140 | valid_config_auth[ip.DOMAIN][CONF_USERNAME] = MOCK_USERNAME 141 | valid_config_auth[ip.DOMAIN][CONF_PASSWORD] = MOCK_PASSWORD 142 | with patch('homeassistant.components.image_processing.' 143 | 'classificationbox.get_models', 144 | return_value=MOCK_MODEL): 145 | await async_setup_component(hass, ip.DOMAIN, valid_config_auth) 146 | assert hass.states.get(VALID_ENTITY_ID) 147 | 148 | 149 | async def test_process_image(hass, mock_image, mock_healthybox): 150 | """Test processing of an image.""" 151 | with patch('homeassistant.components.image_processing.' 152 | 'classificationbox.get_models', 153 | return_value=MOCK_MODEL): 154 | await async_setup_component(hass, ip.DOMAIN, VALID_CONFIG) 155 | await hass.async_block_till_done() 156 | assert hass.states.get(VALID_ENTITY_ID) 157 | 158 | classification_events = [] 159 | 160 | @callback 161 | def mock_classification_event(event): 162 | """Mock event.""" 163 | classification_events.append(event) 164 | 165 | hass.bus.async_listen('image_processing.image_classification', 166 | mock_classification_event) 167 | 168 | with requests_mock.Mocker() as mock_req: 169 | url = 'http://{}:{}/{}/models/{}/predict'.format( 170 | MOCK_IP, 171 | MOCK_PORT, 172 | cb.CLASSIFIER, 173 | MOCK_MODEL_ID) 174 | mock_req.post(url, json=MOCK_JSON) 175 | data = {ATTR_ENTITY_ID: VALID_ENTITY_ID} 176 | await hass.services.async_call(ip.DOMAIN, 177 | ip.SERVICE_SCAN, 178 | service_data=data) 179 | await hass.async_block_till_done() 180 | 181 | state = hass.states.get(VALID_ENTITY_ID) 182 | assert state.state == 'birds' 183 | assert state.attributes.get(ip.ATTR_CONFIDENCE) == ip.DEFAULT_CONFIDENCE 184 | 185 | assert state.attributes.get(cb.ATTR_MODEL_ID) == MOCK_MODEL_ID 186 | assert (state.attributes.get(CONF_FRIENDLY_NAME) == 187 | 'classificationbox demo_camera 12345') 188 | 189 | assert len(classification_events) == 1 190 | assert classification_events[0].data[ATTR_ID] == 'birds' 191 | assert classification_events[0].data[ip.ATTR_CONFIDENCE] == 91.59 192 | 193 | 194 | async def test_process_image_errors(hass, mock_healthybox, mock_image, caplog): 195 | """Test post_image errors.""" 196 | with patch('homeassistant.components.image_processing.' 197 | 'classificationbox.get_models', 198 | return_value=MOCK_MODEL): 199 | await async_setup_component(hass, ip.DOMAIN, VALID_CONFIG) 200 | assert hass.states.get(VALID_ENTITY_ID) 201 | 202 | # Test connection error. 203 | with requests_mock.Mocker() as mock_req: 204 | url = 'http://{}:{}/{}/models/{}/predict'.format( 205 | MOCK_IP, 206 | MOCK_PORT, 207 | cb.CLASSIFIER, 208 | MOCK_MODEL_ID) 209 | mock_req.register_uri( 210 | 'POST', url, exc=requests.exceptions.ConnectTimeout) 211 | data = {ATTR_ENTITY_ID: VALID_ENTITY_ID} 212 | await hass.services.async_call(ip.DOMAIN, 213 | ip.SERVICE_SCAN, 214 | service_data=data) 215 | await hass.async_block_till_done() 216 | assert "ConnectionError: Is classificationbox running?" in caplog.text 217 | 218 | mock_req.register_uri('POST', url, exc=ValueError) 219 | await hass.services.async_call(ip.DOMAIN, 220 | ip.SERVICE_SCAN, 221 | service_data=data) 222 | await hass.async_block_till_done() 223 | assert "Error with classificationbox query" in caplog.text 224 | -------------------------------------------------------------------------------- /usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robmarkcole/HASS-Machinebox-Classificationbox/4ea59ac549366c374acadc4b8b7951515dd23247/usage.png --------------------------------------------------------------------------------