├── .gitattributes ├── Mechanical ├── Base.stl ├── Camera_Shell.stl └── Electronics_Holder.stl ├── Training ├── reparo │ ├── char1549134944.1250699.png │ ├── char1549134948.5388265.png │ ├── char1549134953.415819.png │ ├── char1549134957.085023.png │ ├── char1549134960.6281433.png │ ├── char1549135814.2520974.png │ ├── char1549135818.3932326.png │ └── char1549135822.7581322.png ├── revelio │ ├── char1549134593.627627.png │ ├── char1549135778.847193.png │ ├── char1549135693.3732228.png │ ├── char1549135698.2345128.png │ ├── char1549135702.8896246.png │ ├── char1549135770.7044222.png │ ├── char1549135774.2924924.png │ ├── char1549137745.6538255revelio.png │ ├── char1549142590.5860612mistakes.png │ ├── char1549146464.8797355silencio.png │ ├── char1549149193.798911mistakes.png │ ├── char1549151053.1350963mistakes.png │ ├── char1549151058.0960536mistakes.png │ ├── char1549151073.4020333mistakes.png │ ├── char1549151081.4312234mistakes.png │ ├── char1549151209.3119826revelio.png │ ├── char1549151220.5886223mistakes.png │ ├── char1549409667.232906mistakes.png │ ├── char1549486587.4702342silencio.png │ ├── char1549486751.1223688mistakes.png │ ├── char1549486773.3973303mistakes.png │ └── char1549151099.9129193tarantallegra.png ├── aguamenti │ ├── char1549135013.588394.png │ ├── char1549134936.9390552.png │ ├── char1549135004.3973796.png │ ├── char1549135007.4959111.png │ ├── char1549135010.5172393.png │ ├── char1549487456.6765552revelio.png │ ├── char1549137735.259938aguamenti.png │ ├── char1549137814.9706757aguamenti.png │ ├── char1549486546.5610905mistakes.png │ ├── char1549487418.3275704mistakes.png │ ├── char1549499980.3219154mistakes.png │ └── char1549499982.8536932mistakes.png ├── incendio │ ├── char1549134830.7029963.png │ ├── char1549134833.2924404.png │ ├── char1549134839.8949878.png │ ├── char1549134842.3847487.png │ ├── char1549134846.3832202.png │ ├── char1549135650.6095092.png │ ├── char1549135653.5555952.png │ ├── char1549135831.705838.png │ ├── char1549137724.257268alohomora.png │ ├── char1549137727.514119incendio.png │ ├── char1549146714.030931aguamenti.png │ ├── char1549230167.064071mistakes.png │ ├── char1549230169.1150951mistakes.png │ ├── char1549230221.689859mistakes.png │ ├── char1549409251.2343516mistakes.png │ ├── char1549230001.5958145aguamenti.png │ ├── char1549230184.6198344aguamenti.png │ ├── char1549230191.3396623aguamenti.png │ ├── char1549230193.9984605aguamenti.png │ └── char1549230197.3656993aguamenti.png ├── locomotor │ ├── char1549135736.192638.png │ ├── char1549135747.390701.png │ ├── char1549135723.9167833.png │ ├── char1549135728.6373637.png │ ├── char1549135732.4599488.png │ ├── char1549135739.9898183.png │ └── char1549135743.8866658.png ├── mistakes │ ├── char1549134828.1858654.png │ ├── char1549134834.930122.png │ ├── char1549134837.2791853.png │ ├── char1549134844.6820228.png │ ├── char1549134882.5678132.png │ ├── char1549134910.688054.png │ ├── char1549134973.934889.png │ ├── char1549134976.0651248.png │ ├── char1549134978.8147955.png │ ├── char1549135003.2003794.png │ ├── char1549135008.7417598.png │ ├── char1549135027.4677393.png │ ├── char1549135030.4910522.png │ ├── char1549135038.041438.png │ ├── char1549135041.0998936.png │ ├── char1549135046.2201357.png │ ├── char1549135047.166979.png │ ├── char1549135639.6146317.png │ ├── char1549135691.0476217.png │ ├── char1549135721.8296075.png │ ├── char1549135753.6434438.png │ ├── char1549135768.014347.png │ ├── char1549135792.8555179.png │ ├── char1549135830.1199348.png │ ├── char1549137722.4114518mistakes.png │ ├── char1549137747.9032006mistakes.png │ ├── char1549137800.6877744mistakes.png │ ├── char1549137813.4541504mistakes.png │ ├── char1549137819.2642457mistakes.png │ ├── char1549137831.3954854mistakes.png │ ├── char1549146977.947944revelio.png │ ├── char1549146992.590443alohomora.png │ ├── char1549147019.6065128mistakes.png │ ├── char1549147026.184643mistakes.png │ ├── char1549147701.3677227incendio.png │ ├── char1549230042.9189465revelio.png │ ├── char1549327010.939841incendio.png │ ├── char1549416296.149202locomotor.png │ ├── char1549416537.7098784silencio.png │ ├── char1549487411.9165318incendio.png │ ├── char1549487414.340826revelio.png │ ├── char1549499599.8672352silencio.png │ ├── char1549137782.2754471locomotor.png │ ├── char1549146800.2483766aguamenti.png │ ├── char1549147011.4219592locomotor.png │ ├── char1549147023.0353093locomotor.png │ ├── char1549416645.3506453aguamenti.png │ └── char1549416669.4075499tarantallegra.png ├── silencio │ ├── char1549134597.2801886.png │ ├── char1549134865.4267635.png │ ├── char1549134876.3297288.png │ ├── char1549135837.8423452.png │ ├── char1549137742.6285825silencio.png │ ├── char1549146842.7805774silencio.png │ ├── char1549149271.117368mistakes.png │ ├── char1549236759.628093mistakes.png │ ├── char1549408003.4074733mistakes.png │ ├── char1549142607.3978462aguamenti.png │ ├── char1549142617.3608131aguamenti.png │ ├── char1549142624.2391267aguamenti.png │ ├── char1549142633.8884232aguamenti.png │ ├── char1549142636.6425261aguamenti.png │ └── char1549407971.2711759aguamenti.png ├── alohomora │ ├── char1549134585.2415426.png │ ├── char1549134589.4862533.png │ ├── char1549135642.5764225.png │ ├── char1549135755.9827247.png │ ├── char1549135760.9066057.png │ ├── char1549151039.9553797reparo.png │ ├── char1549486680.4345381reparo.png │ ├── char1549137732.7887402alohomora.png │ ├── char1549147242.3494647mistakes.png │ ├── char1549486577.6792555mistakes.png │ ├── char1549486650.1230526mistakes.png │ ├── char1549486653.5236094mistakes.png │ ├── char1549486656.6797826mistakes.png │ └── char1549486659.4118743mistakes.png ├── tarantallegra │ ├── char1549135034.4123232.png │ ├── char1549135050.2098033.png │ ├── char1549137755.311904mistakes.png │ ├── char1549137759.8087208mistakes.png │ ├── char1549137767.4617078mistakes.png │ ├── char1549137775.092224mistakes.png │ ├── char1549137777.7461753mistakes.png │ ├── char1549137780.5175655mistakes.png │ ├── char1549137784.0255115mistakes.png │ ├── char1549143604.616682mistakes.png │ ├── char1549137750.5551698alohomora.png │ ├── char1549137771.2325063alohomora.png │ └── char1549146793.8196929tarantallegra.png └── specialis_revelio │ ├── char1549134897.847413.png │ ├── char1549135799.369157.png │ ├── char1549134885.6995673.png │ ├── char1549134890.1387296.png │ ├── char1549134894.1244876.png │ ├── char1549134901.7481587.png │ ├── char1549134905.3567593.png │ ├── char1549135665.7073529.png │ ├── char1549135669.4771724.png │ ├── char1549135673.9261072.png │ ├── char1549135795.2270458.png │ ├── char1549135807.2117774.png │ ├── char1549322067.0230124reparo.png │ ├── char1549322074.3261948reparo.png │ ├── char1549326181.5501492reparo.png │ ├── char1549147061.8016233aguamenti.png │ ├── char1549236718.5753653mistakes.png │ ├── char1549322061.5392897mistakes.png │ ├── char1549322070.5241277mistakes.png │ ├── char1549322077.964091mistakes.png │ ├── char1549407838.7733963revelio.png │ ├── char1549413907.7631967aguamenti.png │ ├── char1549147067.3801441specialis_revelio.png │ └── char1549147305.3645427specialis_revelio.png ├── README.md ├── LICENSE ├── CountsPerSec.py ├── PyPotter.pyproj ├── HassApi.py ├── Home Assistant └── smart_wands.yaml ├── .gitignore └── PyPotter.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Mechanical/Base.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Mechanical/Base.stl -------------------------------------------------------------------------------- /Mechanical/Camera_Shell.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Mechanical/Camera_Shell.stl -------------------------------------------------------------------------------- /Mechanical/Electronics_Holder.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Mechanical/Electronics_Holder.stl -------------------------------------------------------------------------------- /Training/reparo/char1549134944.1250699.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549134944.1250699.png -------------------------------------------------------------------------------- /Training/reparo/char1549134948.5388265.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549134948.5388265.png -------------------------------------------------------------------------------- /Training/reparo/char1549134953.415819.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549134953.415819.png -------------------------------------------------------------------------------- /Training/reparo/char1549134957.085023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549134957.085023.png -------------------------------------------------------------------------------- /Training/reparo/char1549134960.6281433.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549134960.6281433.png -------------------------------------------------------------------------------- /Training/reparo/char1549135814.2520974.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549135814.2520974.png -------------------------------------------------------------------------------- /Training/reparo/char1549135818.3932326.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549135818.3932326.png -------------------------------------------------------------------------------- /Training/reparo/char1549135822.7581322.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/reparo/char1549135822.7581322.png -------------------------------------------------------------------------------- /Training/revelio/char1549134593.627627.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549134593.627627.png -------------------------------------------------------------------------------- /Training/revelio/char1549135778.847193.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549135778.847193.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549135013.588394.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549135013.588394.png -------------------------------------------------------------------------------- /Training/incendio/char1549134830.7029963.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549134830.7029963.png -------------------------------------------------------------------------------- /Training/incendio/char1549134833.2924404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549134833.2924404.png -------------------------------------------------------------------------------- /Training/incendio/char1549134839.8949878.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549134839.8949878.png -------------------------------------------------------------------------------- /Training/incendio/char1549134842.3847487.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549134842.3847487.png -------------------------------------------------------------------------------- /Training/incendio/char1549134846.3832202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549134846.3832202.png -------------------------------------------------------------------------------- /Training/incendio/char1549135650.6095092.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549135650.6095092.png -------------------------------------------------------------------------------- /Training/incendio/char1549135653.5555952.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549135653.5555952.png -------------------------------------------------------------------------------- /Training/incendio/char1549135831.705838.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549135831.705838.png -------------------------------------------------------------------------------- /Training/locomotor/char1549135736.192638.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/locomotor/char1549135736.192638.png -------------------------------------------------------------------------------- /Training/locomotor/char1549135747.390701.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/locomotor/char1549135747.390701.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134828.1858654.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134828.1858654.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134834.930122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134834.930122.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134837.2791853.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134837.2791853.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134844.6820228.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134844.6820228.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134882.5678132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134882.5678132.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134910.688054.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134910.688054.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134973.934889.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134973.934889.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134976.0651248.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134976.0651248.png -------------------------------------------------------------------------------- /Training/mistakes/char1549134978.8147955.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549134978.8147955.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135003.2003794.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135003.2003794.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135008.7417598.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135008.7417598.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135027.4677393.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135027.4677393.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135030.4910522.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135030.4910522.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135038.041438.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135038.041438.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135041.0998936.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135041.0998936.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135046.2201357.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135046.2201357.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135047.166979.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135047.166979.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135639.6146317.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135639.6146317.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135691.0476217.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135691.0476217.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135721.8296075.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135721.8296075.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135753.6434438.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135753.6434438.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135768.014347.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135768.014347.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135792.8555179.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135792.8555179.png -------------------------------------------------------------------------------- /Training/mistakes/char1549135830.1199348.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549135830.1199348.png -------------------------------------------------------------------------------- /Training/revelio/char1549135693.3732228.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549135693.3732228.png -------------------------------------------------------------------------------- /Training/revelio/char1549135698.2345128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549135698.2345128.png -------------------------------------------------------------------------------- /Training/revelio/char1549135702.8896246.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549135702.8896246.png -------------------------------------------------------------------------------- /Training/revelio/char1549135770.7044222.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549135770.7044222.png -------------------------------------------------------------------------------- /Training/revelio/char1549135774.2924924.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549135774.2924924.png -------------------------------------------------------------------------------- /Training/silencio/char1549134597.2801886.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549134597.2801886.png -------------------------------------------------------------------------------- /Training/silencio/char1549134865.4267635.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549134865.4267635.png -------------------------------------------------------------------------------- /Training/silencio/char1549134876.3297288.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549134876.3297288.png -------------------------------------------------------------------------------- /Training/silencio/char1549135837.8423452.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549135837.8423452.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549134936.9390552.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549134936.9390552.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549135004.3973796.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549135004.3973796.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549135007.4959111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549135007.4959111.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549135010.5172393.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549135010.5172393.png -------------------------------------------------------------------------------- /Training/alohomora/char1549134585.2415426.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549134585.2415426.png -------------------------------------------------------------------------------- /Training/alohomora/char1549134589.4862533.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549134589.4862533.png -------------------------------------------------------------------------------- /Training/alohomora/char1549135642.5764225.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549135642.5764225.png -------------------------------------------------------------------------------- /Training/alohomora/char1549135755.9827247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549135755.9827247.png -------------------------------------------------------------------------------- /Training/alohomora/char1549135760.9066057.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549135760.9066057.png -------------------------------------------------------------------------------- /Training/locomotor/char1549135723.9167833.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/locomotor/char1549135723.9167833.png -------------------------------------------------------------------------------- /Training/locomotor/char1549135728.6373637.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/locomotor/char1549135728.6373637.png -------------------------------------------------------------------------------- /Training/locomotor/char1549135732.4599488.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/locomotor/char1549135732.4599488.png -------------------------------------------------------------------------------- /Training/locomotor/char1549135739.9898183.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/locomotor/char1549135739.9898183.png -------------------------------------------------------------------------------- /Training/locomotor/char1549135743.8866658.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/locomotor/char1549135743.8866658.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549135034.4123232.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549135034.4123232.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549135050.2098033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549135050.2098033.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549487456.6765552revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549487456.6765552revelio.png -------------------------------------------------------------------------------- /Training/alohomora/char1549151039.9553797reparo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549151039.9553797reparo.png -------------------------------------------------------------------------------- /Training/alohomora/char1549486680.4345381reparo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549486680.4345381reparo.png -------------------------------------------------------------------------------- /Training/incendio/char1549137724.257268alohomora.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549137724.257268alohomora.png -------------------------------------------------------------------------------- /Training/incendio/char1549137727.514119incendio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549137727.514119incendio.png -------------------------------------------------------------------------------- /Training/incendio/char1549146714.030931aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549146714.030931aguamenti.png -------------------------------------------------------------------------------- /Training/incendio/char1549230167.064071mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230167.064071mistakes.png -------------------------------------------------------------------------------- /Training/incendio/char1549230169.1150951mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230169.1150951mistakes.png -------------------------------------------------------------------------------- /Training/incendio/char1549230221.689859mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230221.689859mistakes.png -------------------------------------------------------------------------------- /Training/incendio/char1549409251.2343516mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549409251.2343516mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549137722.4114518mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549137722.4114518mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549137747.9032006mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549137747.9032006mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549137800.6877744mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549137800.6877744mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549137813.4541504mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549137813.4541504mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549137819.2642457mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549137819.2642457mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549137831.3954854mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549137831.3954854mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549146977.947944revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549146977.947944revelio.png -------------------------------------------------------------------------------- /Training/mistakes/char1549146992.590443alohomora.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549146992.590443alohomora.png -------------------------------------------------------------------------------- /Training/mistakes/char1549147019.6065128mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549147019.6065128mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549147026.184643mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549147026.184643mistakes.png -------------------------------------------------------------------------------- /Training/mistakes/char1549147701.3677227incendio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549147701.3677227incendio.png -------------------------------------------------------------------------------- /Training/mistakes/char1549230042.9189465revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549230042.9189465revelio.png -------------------------------------------------------------------------------- /Training/mistakes/char1549327010.939841incendio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549327010.939841incendio.png -------------------------------------------------------------------------------- /Training/mistakes/char1549416296.149202locomotor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549416296.149202locomotor.png -------------------------------------------------------------------------------- /Training/mistakes/char1549416537.7098784silencio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549416537.7098784silencio.png -------------------------------------------------------------------------------- /Training/mistakes/char1549487411.9165318incendio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549487411.9165318incendio.png -------------------------------------------------------------------------------- /Training/mistakes/char1549487414.340826revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549487414.340826revelio.png -------------------------------------------------------------------------------- /Training/mistakes/char1549499599.8672352silencio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549499599.8672352silencio.png -------------------------------------------------------------------------------- /Training/revelio/char1549137745.6538255revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549137745.6538255revelio.png -------------------------------------------------------------------------------- /Training/revelio/char1549142590.5860612mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549142590.5860612mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549146464.8797355silencio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549146464.8797355silencio.png -------------------------------------------------------------------------------- /Training/revelio/char1549149193.798911mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549149193.798911mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549151053.1350963mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549151053.1350963mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549151058.0960536mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549151058.0960536mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549151073.4020333mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549151073.4020333mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549151081.4312234mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549151081.4312234mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549151209.3119826revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549151209.3119826revelio.png -------------------------------------------------------------------------------- /Training/revelio/char1549151220.5886223mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549151220.5886223mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549409667.232906mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549409667.232906mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549486587.4702342silencio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549486587.4702342silencio.png -------------------------------------------------------------------------------- /Training/revelio/char1549486751.1223688mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549486751.1223688mistakes.png -------------------------------------------------------------------------------- /Training/revelio/char1549486773.3973303mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549486773.3973303mistakes.png -------------------------------------------------------------------------------- /Training/silencio/char1549137742.6285825silencio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549137742.6285825silencio.png -------------------------------------------------------------------------------- /Training/silencio/char1549146842.7805774silencio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549146842.7805774silencio.png -------------------------------------------------------------------------------- /Training/silencio/char1549149271.117368mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549149271.117368mistakes.png -------------------------------------------------------------------------------- /Training/silencio/char1549236759.628093mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549236759.628093mistakes.png -------------------------------------------------------------------------------- /Training/silencio/char1549408003.4074733mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549408003.4074733mistakes.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549134897.847413.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549134897.847413.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549135799.369157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549135799.369157.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549137735.259938aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549137735.259938aguamenti.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549137814.9706757aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549137814.9706757aguamenti.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549486546.5610905mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549486546.5610905mistakes.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549487418.3275704mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549487418.3275704mistakes.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549499980.3219154mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549499980.3219154mistakes.png -------------------------------------------------------------------------------- /Training/aguamenti/char1549499982.8536932mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/aguamenti/char1549499982.8536932mistakes.png -------------------------------------------------------------------------------- /Training/alohomora/char1549137732.7887402alohomora.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549137732.7887402alohomora.png -------------------------------------------------------------------------------- /Training/alohomora/char1549147242.3494647mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549147242.3494647mistakes.png -------------------------------------------------------------------------------- /Training/alohomora/char1549486577.6792555mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549486577.6792555mistakes.png -------------------------------------------------------------------------------- /Training/alohomora/char1549486650.1230526mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549486650.1230526mistakes.png -------------------------------------------------------------------------------- /Training/alohomora/char1549486653.5236094mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549486653.5236094mistakes.png -------------------------------------------------------------------------------- /Training/alohomora/char1549486656.6797826mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549486656.6797826mistakes.png -------------------------------------------------------------------------------- /Training/alohomora/char1549486659.4118743mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/alohomora/char1549486659.4118743mistakes.png -------------------------------------------------------------------------------- /Training/incendio/char1549230001.5958145aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230001.5958145aguamenti.png -------------------------------------------------------------------------------- /Training/incendio/char1549230184.6198344aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230184.6198344aguamenti.png -------------------------------------------------------------------------------- /Training/incendio/char1549230191.3396623aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230191.3396623aguamenti.png -------------------------------------------------------------------------------- /Training/incendio/char1549230193.9984605aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230193.9984605aguamenti.png -------------------------------------------------------------------------------- /Training/incendio/char1549230197.3656993aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/incendio/char1549230197.3656993aguamenti.png -------------------------------------------------------------------------------- /Training/mistakes/char1549137782.2754471locomotor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549137782.2754471locomotor.png -------------------------------------------------------------------------------- /Training/mistakes/char1549146800.2483766aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549146800.2483766aguamenti.png -------------------------------------------------------------------------------- /Training/mistakes/char1549147011.4219592locomotor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549147011.4219592locomotor.png -------------------------------------------------------------------------------- /Training/mistakes/char1549147023.0353093locomotor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549147023.0353093locomotor.png -------------------------------------------------------------------------------- /Training/mistakes/char1549416645.3506453aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549416645.3506453aguamenti.png -------------------------------------------------------------------------------- /Training/silencio/char1549142607.3978462aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549142607.3978462aguamenti.png -------------------------------------------------------------------------------- /Training/silencio/char1549142617.3608131aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549142617.3608131aguamenti.png -------------------------------------------------------------------------------- /Training/silencio/char1549142624.2391267aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549142624.2391267aguamenti.png -------------------------------------------------------------------------------- /Training/silencio/char1549142633.8884232aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549142633.8884232aguamenti.png -------------------------------------------------------------------------------- /Training/silencio/char1549142636.6425261aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549142636.6425261aguamenti.png -------------------------------------------------------------------------------- /Training/silencio/char1549407971.2711759aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/silencio/char1549407971.2711759aguamenti.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549134885.6995673.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549134885.6995673.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549134890.1387296.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549134890.1387296.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549134894.1244876.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549134894.1244876.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549134901.7481587.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549134901.7481587.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549134905.3567593.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549134905.3567593.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549135665.7073529.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549135665.7073529.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549135669.4771724.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549135669.4771724.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549135673.9261072.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549135673.9261072.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549135795.2270458.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549135795.2270458.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549135807.2117774.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549135807.2117774.png -------------------------------------------------------------------------------- /Training/mistakes/char1549416669.4075499tarantallegra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/mistakes/char1549416669.4075499tarantallegra.png -------------------------------------------------------------------------------- /Training/revelio/char1549151099.9129193tarantallegra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/revelio/char1549151099.9129193tarantallegra.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137755.311904mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137755.311904mistakes.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137759.8087208mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137759.8087208mistakes.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137767.4617078mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137767.4617078mistakes.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137775.092224mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137775.092224mistakes.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137777.7461753mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137777.7461753mistakes.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137780.5175655mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137780.5175655mistakes.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137784.0255115mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137784.0255115mistakes.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549143604.616682mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549143604.616682mistakes.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549322067.0230124reparo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549322067.0230124reparo.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549322074.3261948reparo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549322074.3261948reparo.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549326181.5501492reparo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549326181.5501492reparo.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137750.5551698alohomora.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137750.5551698alohomora.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549137771.2325063alohomora.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549137771.2325063alohomora.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549147061.8016233aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549147061.8016233aguamenti.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549236718.5753653mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549236718.5753653mistakes.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549322061.5392897mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549322061.5392897mistakes.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549322070.5241277mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549322070.5241277mistakes.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549322077.964091mistakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549322077.964091mistakes.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549407838.7733963revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549407838.7733963revelio.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549413907.7631967aguamenti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549413907.7631967aguamenti.png -------------------------------------------------------------------------------- /Training/tarantallegra/char1549146793.8196929tarantallegra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/tarantallegra/char1549146793.8196929tarantallegra.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549147067.3801441specialis_revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549147067.3801441specialis_revelio.png -------------------------------------------------------------------------------- /Training/specialis_revelio/char1549147305.3645427specialis_revelio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthole/PyPotter/HEAD/Training/specialis_revelio/char1549147305.3645427specialis_revelio.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyPotter 2 | 3 | PyPotter is a Python project allowing you to control your Home Assistant based smart home via a magic wand. 4 | 5 | For now, more complete information can be found here: https://www.adamthole.com/category/smart-home/ 6 | 7 | # Acknowledgements 8 | PyPotter shares inspiration and code from the following projects: 9 | 10 | Raspberry Potter - https://github.com/sean-obrien/rpotter/ 11 | pi_to_potter - https://github.com/mamacker/pi_to_potter 12 | computer-vision - https://github.com/nrsyed/computer-vision 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Adam Thole 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 | -------------------------------------------------------------------------------- /CountsPerSec.py: -------------------------------------------------------------------------------- 1 | # Original Code: https://github.com/nrsyed/computer-vision/blob/master/multithread/CountsPerSec.py 2 | # Modified for use in PyPotter 3 | # 4 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 5 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 6 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 7 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 8 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 9 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 10 | # THE SOFTWARE. 11 | 12 | from datetime import datetime 13 | 14 | class CountsPerSec: 15 | """ 16 | Class that tracks the number of occurrences ("counts") of an 17 | arbitrary event and returns the frequency in occurrences 18 | (counts) per second. The caller must increment the count. 19 | """ 20 | 21 | def __init__(self): 22 | self._SmoothingFactor = 90 23 | self._timeList = [] 24 | 25 | def countsPerSec(self): 26 | self._timeList.append(datetime.now()) 27 | 28 | if (len(self._timeList) > self._SmoothingFactor): 29 | self._timeList.pop(0) 30 | 31 | elapsed_time = (self._timeList[-1] - self._timeList[0]).total_seconds() 32 | 33 | if (elapsed_time > 0): 34 | return len(self._timeList) / elapsed_time 35 | 36 | return 0 -------------------------------------------------------------------------------- /PyPotter.pyproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Debug 4 | 2.0 5 | c40bd171-41db-40af-b21c-1ab9e18d8e1f 6 | 7 | 8 | PyPotter.py 9 | 10 | 11 | . 12 | . 13 | PyPotter 14 | PythonApplication1 15 | Standard Python launcher 16 | 17 | False 18 | 19 | 20 | true 21 | false 22 | 23 | 24 | true 25 | false 26 | 27 | 28 | 29 | Code 30 | 31 | 32 | Code 33 | 34 | 35 | Code 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /HassApi.py: -------------------------------------------------------------------------------- 1 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 3 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 5 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 6 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 7 | # THE SOFTWARE. 8 | 9 | import requests 10 | import json 11 | 12 | class HassApi: 13 | """ Home Assistant REST API Calls """ 14 | 15 | def __init__(self, url, token): 16 | self.url = url 17 | self.token = token 18 | 19 | def TriggerAutomation(self, name): 20 | url = self.url + "/api/services/automation/trigger" 21 | headers = { 22 | 'Authorization': "Bearer " + self.token, 23 | } 24 | payload = {"entity_id": name} 25 | response = requests.post(url, data=json.dumps(payload), headers=headers) 26 | print(response.text) 27 | 28 | def TurnOnLight(self, name, color): 29 | url = self.url + "/api/services/light/turn_on" 30 | headers = { 31 | 'Authorization': "Bearer " + self.token, 32 | } 33 | payload = {"entity_id": name, "color_name": color} 34 | response = requests.post(url, data=json.dumps(payload), headers=headers) 35 | print(response.text) 36 | 37 | def PlayPlaylist(self, name, contentId): 38 | url = self.url + "/api/services/media_player/play_media" 39 | headers = { 40 | 'Authorization': "Bearer " + self.token, 41 | } 42 | payload = {"entity_id": name, "media_content_type": "playlist", "media_content_id": contentId} 43 | response = requests.post(url, data=json.dumps(payload), headers=headers) 44 | print(response.text) 45 | 46 | def PlaySong(self, name, contentId): 47 | url = self.url + "/api/services/media_player/play_media" 48 | headers = { 49 | 'Authorization': "Bearer " + self.token, 50 | } 51 | payload = {"entity_id": name, "media_content_type": "music", "media_content_id": contentId} 52 | response = requests.post(url, data=json.dumps(payload), headers=headers) 53 | print(response.text) 54 | 55 | def PauseMedia(self, name): 56 | url = self.url + "/api/services/media_player/media_pause" 57 | headers = { 58 | 'Authorization': "Bearer " + self.token, 59 | } 60 | payload = {"entity_id": name} 61 | response = requests.post(url, data=json.dumps(payload), headers=headers) 62 | print(response.text) 63 | 64 | def PlayMedia(self, name): 65 | url = self.url + "/api/services/media_player/media_play" 66 | headers = { 67 | 'Authorization': "Bearer " + self.token, 68 | } 69 | payload = {"entity_id": name} 70 | response = requests.post(url, data=json.dumps(payload), headers=headers) 71 | print(response.text) 72 | 73 | def TurnOnSwitch(self, name): 74 | url = self.url + "/api/services/switch/turn_on" 75 | headers = { 76 | 'Authorization': "Bearer " + self.token, 77 | } 78 | payload = {"entity_id": name} 79 | response = requests.post(url, data=json.dumps(payload), headers=headers) 80 | print(response.text) 81 | 82 | -------------------------------------------------------------------------------- /Home Assistant/smart_wands.yaml: -------------------------------------------------------------------------------- 1 | automation: 2 | - alias: wand_aguamenti 3 | trigger: 4 | action: 5 | - service: light.turn_on 6 | data: 7 | entity_id: light.office 8 | color_name: white 9 | - service: light.turn_on 10 | data: 11 | entity_id: light.office 12 | color_name: blue 13 | - service: timer.start 14 | entity_id: timer.reset_lights 15 | 16 | - alias: wand_revelio 17 | trigger: 18 | action: 19 | - service: light.turn_off 20 | entity_id: light.office 21 | - service: timer.start 22 | data: 23 | entity_id: timer.reset_lights 24 | 25 | - alias: wand_tarantallegra 26 | trigger: 27 | action: 28 | # - service: switch.turn_on 29 | # entity_id: switch.coffee_roaster_switch 30 | - service: light.turn_on 31 | data: 32 | entity_id: light.office 33 | color_name: white 34 | - service: light.turn_on 35 | data: 36 | entity_id: light.office 37 | color_name: orange 38 | # - service: timer.start 39 | # entity_id: timer.turn_off_popcorn 40 | 41 | - alias: wand_incendio 42 | trigger: 43 | action: 44 | - service: light.turn_on 45 | data: 46 | entity_id: light.office 47 | color_name: white 48 | - service: light.turn_on 49 | data: 50 | entity_id: light.office 51 | color_name: red 52 | - service: timer.start 53 | data: 54 | entity_id: timer.reset_lights 55 | 56 | - alias: wand_specialis_revelio 57 | trigger: 58 | action: 59 | - service: light.turn_on 60 | data: 61 | entity_id: light.office 62 | color_name: white 63 | - service: light.turn_on 64 | data: 65 | entity_id: light.office 66 | color_name: purple 67 | - service: timer.start 68 | data: 69 | entity_id: timer.reset_lights 70 | 71 | - alias: wand_alohomora 72 | trigger: 73 | action: 74 | - service: light.turn_on 75 | data: 76 | entity_id: light.office 77 | color_name: white 78 | - service: light.turn_on 79 | data: 80 | entity_id: light.office 81 | color_name: green 82 | - service: timer.start 83 | entity_id: timer.reset_lights 84 | 85 | - alias: wand_silencio 86 | trigger: 87 | action: 88 | # - service: switch.turn_off 89 | # entity_id: switch.windrunner_switch 90 | # - service: switch.turn_off 91 | # entity_id: switch.coffee_roaster_switch 92 | - service: light.turn_on 93 | data: 94 | entity_id: light.office 95 | kelvin: 2700 96 | 97 | - alias: Reset Lights After Spell Light Timer 98 | initial_state: on 99 | trigger: 100 | platform: event 101 | event_type: timer.finished 102 | event_data: 103 | entity_id: timer.reset_lights 104 | action: 105 | - service: light.turn_on 106 | data: 107 | entity_id: light.office 108 | kelvin: 2700 109 | 110 | # - alias: Timer Water Action 111 | # trigger: 112 | # platform: event 113 | # event_type: timer.finished 114 | # event_data: 115 | # entity_id: timer.turn_off_water 116 | # action: 117 | # - service: switch.turn_off 118 | # entity_id: switch.windrunner_switch 119 | # 120 | # - alias: Timer Popcorn Action 121 | # trigger: 122 | # platform: event 123 | # event_type: timer.finished 124 | # event_data: 125 | # entity_id: timer.turn_off_popcorn 126 | # action: 127 | # - service: switch.turn_off 128 | # entity_id: switch.coffee_roaster_switch 129 | 130 | timer: 131 | reset_lights: 132 | duration: '00:00:15' 133 | # turn_off_water: 134 | # duration: '00:00:15' 135 | # turn_off_popcorn: 136 | # duration: '00:00:30' 137 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | 336 | # Visual Studio Python Project 337 | .pyproj -------------------------------------------------------------------------------- /PyPotter.py: -------------------------------------------------------------------------------- 1 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 3 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 5 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 6 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 7 | # THE SOFTWARE. 8 | 9 | import sys 10 | import cv2 11 | from cv2 import * 12 | import numpy as np 13 | import math 14 | import os 15 | from os import listdir 16 | from os.path import isfile, join, isdir 17 | import time 18 | import datetime 19 | import threading 20 | from threading import Thread 21 | from statistics import mean 22 | from CountsPerSec import CountsPerSec 23 | from HassApi import HassApi 24 | 25 | # Check for required number of arguments 26 | if (len(sys.argv) < 4): 27 | print("Incorrect number of arguments. Required Arguments: [video source url] [home assistant URL] [API token]") 28 | sys.exit(0) 29 | 30 | # Parse Required Arguments 31 | videoSource = sys.argv[1] 32 | hassUrl = sys.argv[2] 33 | hassRestToken = sys.argv[3] 34 | 35 | # Parse Optional Arguments 36 | IsRemoveBackground = True 37 | IsShowOutputWindows = True 38 | IsTraining = False 39 | IsDebugFps = False 40 | 41 | if (len(sys.argv) >= 5): 42 | IsRemoveBackground = sys.argv[4] == "True" 43 | 44 | if (len(sys.argv) >= 6): 45 | IsShowOutputWindows = sys.argv[5] == "True" 46 | 47 | if (len(sys.argv) >= 7): 48 | IsTraining = sys.argv[6] == "True" 49 | 50 | if (len(sys.argv) >= 8): 51 | IsDebugFps = sys.argv[7] == "True" 52 | 53 | # Initialize Home Assistant Rest API Wrapper 54 | hass = HassApi(hassUrl, hassRestToken) 55 | 56 | # Constants 57 | DesiredFps = 42 58 | DefaultFps = 42 # Original constants trained for 42 FPS 59 | MicroSecondsBetweenFrames = (1 / DesiredFps) * 1000000 60 | 61 | TrainingResolution = 50 62 | TrainingNumPixels = TrainingResolution * TrainingResolution 63 | TrainingFolderName = "Training" 64 | SpellEndMovement = 0.5 * (DefaultFps / DesiredFps ) 65 | MinSpellLength = 15 * (DesiredFps / DefaultFps) 66 | MinSpellDistance = 100 67 | NumDistancesToAverage = int(round( 20 * (DesiredFps / DefaultFps))) 68 | 69 | # Booleans to turn on or off output windows 70 | IsShowOriginal = False 71 | IsShowBackgroundRemoved = False 72 | IsShowThreshold = False 73 | IsShowOutput = False 74 | 75 | if IsShowOutputWindows: 76 | IsShowOriginal = True 77 | IsShowBackgroundRemoved = True 78 | IsShowThreshold = True 79 | IsShowOutput = True 80 | 81 | # Create Windows 82 | if (IsShowOriginal): 83 | cv2.namedWindow("Original") 84 | cv2.moveWindow("Original", 0, 0) 85 | 86 | if (IsShowBackgroundRemoved): 87 | cv2.namedWindow("BackgroundRemoved") 88 | cv2.moveWindow("BackgroundRemoved", 640, 0) 89 | 90 | if (IsShowThreshold): 91 | cv2.namedWindow("Threshold") 92 | cv2.moveWindow("Threshold", 0, 480+30) 93 | 94 | if (IsShowOutput): 95 | cv2.namedWindow("Output") 96 | cv2.moveWindow("Output", 640, 480+30) 97 | 98 | # Init Global Variables 99 | IsNewFrame = False 100 | nameLookup = {} 101 | LastSpell = "None" 102 | 103 | originalCps = CountsPerSec() 104 | noBackgroundCps = CountsPerSec() 105 | thresholdCps = CountsPerSec() 106 | outputCps = CountsPerSec() 107 | 108 | lk_params = dict( winSize = (25,25), 109 | maxLevel = 7, 110 | criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) 111 | 112 | IsNewFrame = False 113 | frame = None 114 | 115 | IsNewFrameNoBackground = False 116 | frame_no_background = None 117 | 118 | IsNewFrameThreshold = False 119 | frameThresh = None 120 | 121 | findNewWands = True 122 | trackedPoints = None 123 | wandTracks = [] 124 | 125 | def InitClassificationAlgo() : 126 | """ 127 | Create and Train k-Nearest Neighbor Algorithm 128 | """ 129 | global knn, nameLookup 130 | labelNames = [] 131 | labelIndexes = [] 132 | trainingSet = [] 133 | numPics = 0 134 | dirCount = 0 135 | scriptpath = os.path.realpath(__file__) 136 | trainingDirectory = join(os.path.dirname(scriptpath), TrainingFolderName) 137 | 138 | # Every folder in the training directory contains a set of images corresponding to a single spell. 139 | # Loop through all folders to train all spells. 140 | for d in listdir(trainingDirectory): 141 | if isdir(join(trainingDirectory, d)): 142 | nameLookup[dirCount] = d 143 | dirCount = dirCount + 1 144 | for f in listdir(join(trainingDirectory,d)): 145 | if isfile(join(trainingDirectory,d,f)): 146 | labelNames.append(d) 147 | labelIndexes.append(dirCount-1) 148 | trainingSet.append(join(trainingDirectory,d,f)); 149 | numPics = numPics + 1 150 | 151 | print ("Trained Spells: ") 152 | print (nameLookup) 153 | 154 | samples = [] 155 | for i in range(0, numPics): 156 | img = cv2.imread(trainingSet[i]) 157 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 158 | samples.append(gray); 159 | npArray = np.array(samples) 160 | shapedArray = npArray.reshape(-1,TrainingNumPixels).astype(np.float32); 161 | 162 | # Create KNN and Train 163 | knn = cv2.ml.KNearest_create() 164 | knn.train(shapedArray, cv2.ml.ROW_SAMPLE, np.array(labelIndexes)) 165 | 166 | def ClassifyImage(img): 167 | """ 168 | Classify input image based on previously trained k-Nearest Neighbor Algorithm 169 | """ 170 | global knn, nameLookup, args 171 | 172 | if (img.size <= 0): 173 | return "Error" 174 | 175 | size = (TrainingResolution, TrainingResolution) 176 | test_gray = cv2.resize(img,size,interpolation=cv2.INTER_LINEAR) 177 | 178 | imgArr = np.array(test_gray).astype(np.float32) 179 | sample = imgArr.reshape(-1, TrainingNumPixels).astype(np.float32) 180 | ret, result, neighbours, dist = knn.findNearest(sample,k=5) 181 | print(ret, result, neighbours, dist) 182 | 183 | if IsTraining: 184 | filename = "char" + str(time.time()) + nameLookup[ret] + ".png" 185 | cv2.imwrite(join(TrainingFolderName, filename), test_gray) 186 | 187 | if nameLookup[ret] is not None: 188 | print("Match: " + nameLookup[ret]) 189 | return nameLookup[ret] 190 | else: 191 | return "error" 192 | 193 | def PerformSpell(spell): 194 | """ 195 | Make the desired Home Assistant REST API call based on the spell 196 | """ 197 | if (spell=="incendio"): 198 | hass.TriggerAutomation("automation.wand_incendio") 199 | elif (spell=="aguamenti"): 200 | hass.TriggerAutomation("automation.wand_aguamenti") 201 | elif (spell=="alohomora"): 202 | hass.TriggerAutomation("automation.wand_alohomora") 203 | elif (spell=="silencio"): 204 | hass.TriggerAutomation("automation.wand_silencio") 205 | elif (spell=="specialis_revelio"): 206 | hass.TriggerAutomation("automation.wand_specialis_revelio") 207 | elif (spell=="revelio"): 208 | hass.TriggerAutomation("automation.wand_revelio") 209 | elif (spell == "tarantallegra"): 210 | hass.TriggerAutomation("automation.wand_tarantallegra") 211 | 212 | def CheckForPattern(wandTracks, exampleFrame): 213 | """ 214 | Check the given wandTracks to see if is is complete, and if it matches a trained spell 215 | """ 216 | global find_new_wands, LastSpell 217 | 218 | if (wandTracks == None or len(wandTracks) == 0): 219 | return 220 | 221 | thickness = 10 222 | croppedMax = TrainingResolution - thickness 223 | 224 | distances = [] 225 | wand_path_frame = np.zeros_like(exampleFrame) 226 | prevTrack = wandTracks[0] 227 | 228 | for track in wandTracks: 229 | x1 = prevTrack[0] 230 | x2 = track[0] 231 | y1 = prevTrack[1] 232 | y2 = track[1] 233 | 234 | # Calculate the distance 235 | distance = math.sqrt((x1 - x2)**2 + (y1 - y2)**2) 236 | distances.append(distance) 237 | 238 | cv2.line(wand_path_frame, (x1, y1),(x2, y2), (255,255,255), thickness) 239 | prevTrack = track 240 | 241 | mostRecentDistances = distances[-NumDistancesToAverage:] 242 | avgMostRecentDistances = mean(mostRecentDistances) 243 | sumDistances = sum(distances) 244 | 245 | contours, hierarchy = cv2.findContours(wand_path_frame,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 246 | 247 | # Determine if wand stopped moving by looking at recent movement (avgMostRecentDistances), and check the length of distances to make sure the spell is reasonably long 248 | if (avgMostRecentDistances < SpellEndMovement and len(distances) > MinSpellLength): 249 | # Make sure wand path is valid and is over the defined minimum distance 250 | if (len(contours) > 0) and sumDistances > MinSpellDistance: 251 | cnt = contours[0] 252 | x,y,w,h = cv2.boundingRect(cnt) 253 | crop = wand_path_frame[y-10:y+h+10,x-30:x+w+30] 254 | result = ClassifyImage(crop); 255 | cv2.putText(wand_path_frame, result, (0,50), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255,255,255)) 256 | 257 | print("Result: ", result, " Most Recent avg: ", avgMostRecentDistances, " Length Distances: ", len(distances), " Sum Distances: ", sumDistances) 258 | print("") 259 | 260 | PerformSpell(result) 261 | LastSpell = result 262 | find_new_wands = True 263 | wandTracks.clear() 264 | 265 | if wand_path_frame is not None: 266 | if (IsShowOutput): 267 | wandPathFrameWithText = AddIterationsPerSecText(wand_path_frame, outputCps.countsPerSec()) 268 | cv2.putText(wandPathFrameWithText, "Last Spell: " + LastSpell, (10, 400), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255)) 269 | cv2.imshow("Output", wandPathFrameWithText) 270 | 271 | return wandTracks 272 | 273 | def RemoveBackground(): 274 | """ 275 | Thread for removing background 276 | """ 277 | global frame, frame_no_background, IsNewFrame, IsNewFrameNoBackground 278 | 279 | fgbg = cv2.createBackgroundSubtractorMOG2() 280 | t = threading.currentThread() 281 | while getattr(t, "do_run", True): 282 | if (IsNewFrame): 283 | IsNewFrame = False 284 | 285 | frameCopy = frame.copy() 286 | 287 | # Subtract Background 288 | fgmask = fgbg.apply(frameCopy, learningRate=0.001) 289 | frame_no_background = cv2.bitwise_and(frameCopy, frameCopy, mask = fgmask) 290 | IsNewFrameNoBackground = True 291 | 292 | if (IsShowBackgroundRemoved): 293 | frameNoBackgroundWithCounts = AddIterationsPerSecText(frame_no_background.copy(), noBackgroundCps.countsPerSec()) 294 | cv2.imshow("BackgroundRemoved", frameNoBackgroundWithCounts) 295 | else: 296 | time.sleep(0.001) 297 | 298 | def CalculateThreshold(): 299 | """ 300 | Thread for calculating frame threshold 301 | """ 302 | global frame, frame_no_background, frameThresh, IsNewFrame, IsNewFrameNoBackground, IsNewFrameThreshold 303 | 304 | t = threading.currentThread() 305 | thresholdValue = 240 306 | while getattr(t, "do_run", True): 307 | if (IsRemoveBackground and IsNewFrameNoBackground) or (not IsRemoveBackground and IsNewFrame): 308 | if IsRemoveBackground: 309 | IsNewFrameNoBackground = False 310 | frame_gray = cv2.cvtColor(frame_no_background, cv2.COLOR_BGR2GRAY) 311 | 312 | if not IsRemoveBackground: 313 | IsNewFrame = False 314 | frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 315 | 316 | ret, frameThresh = cv2.threshold(frame_gray, thresholdValue, 255, cv2.THRESH_BINARY); 317 | 318 | IsNewFrameThreshold = True 319 | if (IsShowThreshold): 320 | frameThreshWithCounts = AddIterationsPerSecText(frameThresh.copy(), thresholdCps.countsPerSec()) 321 | cv2.imshow("Threshold", frameThreshWithCounts) 322 | else: 323 | time.sleep(0.001) 324 | 325 | def ProcessData(): 326 | """ 327 | Thread for processing final frame 328 | """ 329 | global frameThresh, IsNewFrameThreshold, findNewWands, wandTracks, outputFrameCount 330 | 331 | oldFrameThresh = None 332 | trackedPoints = None 333 | t = threading.currentThread() 334 | 335 | while getattr(t, "do_run", True): 336 | if (IsNewFrameThreshold): 337 | if (IsDebugFps): 338 | outputFrameCount = outputFrameCount + 1 339 | 340 | IsNewFrameThreshold = False 341 | localFrameThresh = frameThresh.copy() 342 | 343 | if (findNewWands): 344 | # Identify Potential Wand Tips using GoodFeaturesToTrack 345 | trackedPoints = cv2.goodFeaturesToTrack(localFrameThresh, 5, .01, 30) 346 | if trackedPoints is not None: 347 | findNewWands = False 348 | else: 349 | # calculate optical flow 350 | nextPoints, statusArray, err = cv2.calcOpticalFlowPyrLK(oldFrameThresh, localFrameThresh, trackedPoints, None, **lk_params) 351 | 352 | # Select good points 353 | good_new = nextPoints[statusArray==1] 354 | good_old = trackedPoints[statusArray==1] 355 | 356 | if (len(good_new) > 0): 357 | # draw the tracks 358 | for i,(new,old) in enumerate(zip(good_new,good_old)): 359 | a,b = new.ravel() 360 | c,d = old.ravel() 361 | 362 | wandTracks.append([a, b]) 363 | 364 | # Update which points are tracked 365 | trackedPoints = good_new.copy().reshape(-1,1,2) 366 | 367 | wandTracks = CheckForPattern(wandTracks, localFrameThresh) 368 | 369 | else: 370 | # No Points were tracked, check for a pattern and start searching for wands again 371 | #wandTracks = CheckForPattern(wandTracks, localFrameThresh) 372 | wandTracks = [] 373 | findNewWands = True 374 | 375 | # Store Previous Threshold Frame 376 | oldFrameThresh = localFrameThresh 377 | 378 | 379 | else: 380 | time.sleep(0.001) 381 | 382 | def AddIterationsPerSecText(frame, iterations_per_sec): 383 | """ 384 | Add iterations per second text to lower-left corner of a frame. 385 | """ 386 | cv2.putText(frame, "{:.0f} iterations/sec".format(iterations_per_sec), 387 | (10, 450), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255)) 388 | return frame 389 | 390 | timeLastPrintedFps = datetime.datetime.now() 391 | 392 | inputFrameCount = 0 393 | outputFrameCount = 0 394 | 395 | # Initialize and traing the spell classification algorithm 396 | InitClassificationAlgo() 397 | 398 | # Start thread to remove frame background 399 | if IsRemoveBackground: 400 | RemoveBackgroundThread = Thread(target=RemoveBackground) 401 | RemoveBackgroundThread.do_run = True 402 | RemoveBackgroundThread.daemon = True 403 | RemoveBackgroundThread.start() 404 | 405 | # Start thread to calculate threshold 406 | CalculateThresholdThread = Thread(target=CalculateThreshold) 407 | CalculateThresholdThread.do_run = True 408 | CalculateThresholdThread.daemon = True 409 | CalculateThresholdThread.start() 410 | 411 | # Start thread to process final frame 412 | ProcessDataThread = Thread(target=ProcessData) 413 | ProcessDataThread.do_run = True 414 | ProcessDataThread.daemon = True 415 | ProcessDataThread.start() 416 | 417 | # Set OpenCV video capture source 418 | videoCapture = cv2.VideoCapture(videoSource) 419 | 420 | # Main Loop 421 | while True: 422 | # Get most recent frame 423 | ret, localFrame = videoCapture.read() 424 | 425 | if (ret): 426 | frame = localFrame.copy() 427 | 428 | # If successful, flip the frame and set the Flag for the next process to take over 429 | cv2.flip(frame, 1, frame) # Flipping the frame is done so the spells look like what we expect, instead of the mirror image 430 | IsNewFrame = True 431 | 432 | if (IsDebugFps): 433 | inputFrameCount = inputFrameCount + 1 434 | 435 | # Print FPS Debug info every second 436 | if ((datetime.datetime.now() - timeLastPrintedFps).seconds >= 1 ): 437 | timeLastPrintedFps = datetime.datetime.now() 438 | print("FPS: %d/%d" %(inputFrameCount, outputFrameCount)) 439 | inputFrameCount = 0 440 | outputFrameCount = 0 441 | 442 | 443 | # Update Windows 444 | if (IsShowOriginal): 445 | frameWithCounts = AddIterationsPerSecText(frame.copy(), originalCps.countsPerSec()) 446 | cv2.imshow("Original", frameWithCounts) 447 | 448 | elif not ret: 449 | # If an error occurred, try initializing the video capture again 450 | videoCapture = cv2.VideoCapture(videoSource) 451 | 452 | # Check for ESC key, if pressed shut everything down 453 | if (cv2.waitKey(1) is 27): 454 | break 455 | 456 | # Shutdown PyPotter 457 | if IsRemoveBackground: 458 | RemoveBackgroundThread.do_run = False 459 | RemoveBackgroundThread.join() 460 | 461 | CalculateThresholdThread.do_run = False 462 | ProcessDataThread.do_run = False 463 | 464 | CalculateThresholdThread.join() 465 | ProcessDataThread.join() 466 | 467 | cv2.destroyAllWindows() --------------------------------------------------------------------------------