├── .gitignore ├── .img ├── ir_sig_graph.png ├── rickroll.jpg ├── subghz_histogram.png ├── subghz_plot.png └── try_ir-RC5.png ├── .pylintrc ├── IR ├── .Lego-Train-Remote.png ├── .Midea_rem500.png ├── .Minolta-MN674.png ├── .Roomba_5101IR.png ├── .gen-all-ir.sh ├── .gitignore ├── All-Codes │ ├── IR-NEC-00-00.ir │ ├── IR-NEC-02-00.ir │ ├── IR-NEC-04-00.ir │ ├── IR-NEC-40-00.ir │ ├── IR-NEC-50-00.ir │ ├── IR-NECext-00-00.ir │ ├── IR-NECext-00-7F.ir │ ├── IR-NECext-02-00.ir │ ├── IR-NECext-84-00.ir │ ├── IR-NECext-85-00.ir │ ├── IR-NECext-86-00.ir │ ├── IR-NECext-EA-00.ir │ ├── IR-RC5-00-00.ir │ ├── IR-RC5-03-00.ir │ ├── IR-RC5-30-00.ir │ ├── IR-RC6-00-00.ir │ ├── IR-SIRC-01-00.ir │ ├── IR-SIRC-10-00.ir │ ├── IR-SIRC15-1A-00.ir │ ├── IR-SIRC15-77-00.ir │ ├── IR-SIRC15-97-00.ir │ ├── IR-SIRC15-A4-00.ir │ ├── IR-Samsung32-00-00.ir │ ├── IR-Samsung32-06-00.ir │ ├── IR-Samsung32-07-00.ir │ ├── IR-Samsung32-08-00.ir │ ├── IR-Samsung32-0B-00.ir │ ├── IR-Samsung32-0E-00.ir │ ├── IR-Samsung32-37-07.ir │ └── README.md ├── Lego_Remote.ir ├── Lego_Train.ir ├── Midea_AC.ir ├── Minolta_MN674.ir ├── README.md └── Roomba_5101IR.ir ├── LICENSE ├── Makefile ├── README.md ├── ir_gen_all_codes.py ├── ir_plot.py ├── nfc ├── .rick-roll.jpg ├── README.md └── Rick_Roll.nfc ├── nfc_dict_diff.py ├── nfc_dict_strip.py ├── nfc_flip2prox.py ├── nfc_gen_phone.py ├── nfc_gen_url.py ├── nfc_gen_wifi.py ├── nfc_hexdump.py ├── nfc_prox2flip.py ├── subghz ├── .fan-11T.png ├── .x10-unit.png ├── README.md ├── X10 │ ├── X10_All-LIGHTS-OFF.png │ ├── X10_All-LIGHTS-OFF.sub │ ├── X10_All-LIGHTS-ON.sub │ ├── X10_All-OFF.sub │ └── X10_All-ON.sub ├── fan_bruteforce │ ├── fan_brute-High.sub │ ├── fan_brute-Lit.sub │ ├── fan_brute-Low.sub │ ├── fan_brute-Med.sub │ └── fan_brute-Off.sub └── firecracker_spec.txt ├── subghz_create_dat.py ├── subghz_decode_presets.py ├── subghz_gen_cmd.py ├── subghz_histogram.py ├── subghz_insteon.py ├── subghz_ook_to_sub.py ├── subghz_plot.py ├── subghz_preset_gen.py ├── subghz_secplusv1.py ├── subghz_secplusv2.py ├── subghz_x10.py ├── test_dat ├── README.md ├── Raw_Sample.sub ├── mf-classic-1k-23AD7C86.json └── setting_user └── url2flipnfc.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | .DS_Store 7 | 8 | secplus/ 9 | repos/ 10 | 11 | # C extensions 12 | *-reg 13 | 14 | /*.bin 15 | *.sav 16 | *-sav 17 | *-sav? 18 | 19 | /*-debug 20 | 21 | /x10 22 | /*.nfc 23 | 24 | *Notes 25 | /*.json 26 | 27 | *.so 28 | 29 | secv2-*.sub 30 | 31 | *-- 32 | *--- 33 | 34 | /*.sub 35 | 36 | touch_tunes-* 37 | *.swp 38 | 39 | Sav/ 40 | 41 | # Distribution / packaging 42 | .Python 43 | build/ 44 | develop-eggs/ 45 | dist/ 46 | downloads/ 47 | eggs/ 48 | .eggs/ 49 | lib/ 50 | lib64/ 51 | parts/ 52 | sdist/ 53 | var/ 54 | wheels/ 55 | pip-wheel-metadata/ 56 | share/python-wheels/ 57 | *.egg-info/ 58 | .installed.cfg 59 | *.egg 60 | MANIFEST 61 | 62 | # PyInstaller 63 | # Usually these files are written by a python script from a template 64 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 65 | *.manifest 66 | *.spec 67 | 68 | # Installer logs 69 | pip-log.txt 70 | pip-delete-this-directory.txt 71 | 72 | # Unit test / coverage reports 73 | htmlcov/ 74 | .tox/ 75 | .nox/ 76 | .coverage 77 | .coverage.* 78 | .cache 79 | nosetests.xml 80 | coverage.xml 81 | *.cover 82 | *.py,cover 83 | .hypothesis/ 84 | .pytest_cache/ 85 | 86 | # Translations 87 | *.mo 88 | *.pot 89 | 90 | # Django stuff: 91 | *.log 92 | local_settings.py 93 | db.sqlite3 94 | db.sqlite3-journal 95 | 96 | # Flask stuff: 97 | instance/ 98 | .webassets-cache 99 | 100 | # Scrapy stuff: 101 | .scrapy 102 | 103 | # Sphinx documentation 104 | docs/_build/ 105 | 106 | # PyBuilder 107 | target/ 108 | 109 | # Jupyter Notebook 110 | .ipynb_checkpoints 111 | 112 | # IPython 113 | profile_default/ 114 | ipython_config.py 115 | 116 | # pyenv 117 | .python-version 118 | 119 | # pipenv 120 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 121 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 122 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 123 | # install all needed dependencies. 124 | #Pipfile.lock 125 | 126 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 127 | __pypackages__/ 128 | 129 | # Celery stuff 130 | celerybeat-schedule 131 | celerybeat.pid 132 | 133 | # SageMath parsed files 134 | *.sage.py 135 | 136 | # Environments 137 | .env 138 | .venv 139 | env/ 140 | venv/ 141 | ENV/ 142 | env.bak/ 143 | venv.bak/ 144 | 145 | # Spyder project settings 146 | .spyderproject 147 | .spyproject 148 | 149 | # Rope project settings 150 | .ropeproject 151 | 152 | # mkdocs documentation 153 | /site 154 | 155 | # mypy 156 | .mypy_cache/ 157 | .dmypy.json 158 | dmypy.json 159 | 160 | # Pyre type checker 161 | .pyre/ 162 | -------------------------------------------------------------------------------- /.img/ir_sig_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/.img/ir_sig_graph.png -------------------------------------------------------------------------------- /.img/rickroll.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/.img/rickroll.jpg -------------------------------------------------------------------------------- /.img/subghz_histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/.img/subghz_histogram.png -------------------------------------------------------------------------------- /.img/subghz_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/.img/subghz_plot.png -------------------------------------------------------------------------------- /.img/try_ir-RC5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/.img/try_ir-RC5.png -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [MASTER] 5 | ignore=flipperzero_protobuf_compiled 6 | 7 | [FORMAT] 8 | max-line-length=110 9 | max-module-lines=2000 10 | 11 | [MESSAGES CONTROL] 12 | disable=invalid-name,missing-docstring 13 | 14 | [DESIGN] 15 | max-branches=30 16 | max-attributes=15 17 | max-locals=30 18 | max-public-methods=30 19 | max-statements=90 20 | 21 | -------------------------------------------------------------------------------- /IR/.Lego-Train-Remote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/IR/.Lego-Train-Remote.png -------------------------------------------------------------------------------- /IR/.Midea_rem500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/IR/.Midea_rem500.png -------------------------------------------------------------------------------- /IR/.Minolta-MN674.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/IR/.Minolta-MN674.png -------------------------------------------------------------------------------- /IR/.Roomba_5101IR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/IR/.Roomba_5101IR.png -------------------------------------------------------------------------------- /IR/.gen-all-ir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dest='IR/All-Codes' 4 | 5 | test ${PWD##*/} == 'IR' && dest='All-Codes' 6 | test -t 1 && echo "dest dir: ${dest}" 7 | 8 | mkdir -p ${dest} 9 | pushd ${dest} 10 | 11 | ../../ir_gen_all_codes.py NEC 00 00 12 | ../../ir_gen_all_codes.py NEC 02 00 13 | ../../ir_gen_all_codes.py NEC 04 00 14 | ../../ir_gen_all_codes.py NEC 40 00 15 | ../../ir_gen_all_codes.py NEC 50 00 16 | ../../ir_gen_all_codes.py NECext 00 00 17 | ../../ir_gen_all_codes.py NECext 00 7F 18 | ../../ir_gen_all_codes.py NECext 02 00 19 | ../../ir_gen_all_codes.py NECext 84 00 20 | ../../ir_gen_all_codes.py NECext 85 00 21 | ../../ir_gen_all_codes.py NECext 86 00 22 | ../../ir_gen_all_codes.py NECext EA 00 23 | ../../ir_gen_all_codes.py RC5 00 00 24 | ../../ir_gen_all_codes.py RC5 03 00 25 | ../../ir_gen_all_codes.py RC5 30 00 26 | ../../ir_gen_all_codes.py RC6 00 00 27 | ../../ir_gen_all_codes.py SIRC 01 00 00 00 28 | ../../ir_gen_all_codes.py SIRC 10 00 00 00 29 | ../../ir_gen_all_codes.py SIRC15 1A 00 00 00 30 | ../../ir_gen_all_codes.py SIRC15 77 00 00 00 31 | ../../ir_gen_all_codes.py SIRC15 97 00 00 00 32 | ../../ir_gen_all_codes.py SIRC15 A4 00 00 00 33 | ../../ir_gen_all_codes.py Samsung32 00 00 00 00 34 | ../../ir_gen_all_codes.py Samsung32 06 00 00 00 35 | ../../ir_gen_all_codes.py Samsung32 07 00 00 00 36 | ../../ir_gen_all_codes.py Samsung32 08 00 00 00 37 | ../../ir_gen_all_codes.py Samsung32 0B 00 00 00 38 | ../../ir_gen_all_codes.py Samsung32 0E 00 00 00 39 | ../../ir_gen_all_codes.py Samsung32 37 07 00 00 40 | popd 41 | -------------------------------------------------------------------------------- /IR/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.py 3 | -------------------------------------------------------------------------------- /IR/All-Codes/IR-RC5-00-00.ir: -------------------------------------------------------------------------------- 1 | Filetype: IR signals file 2 | Version: 1 3 | # generated with flipper_toolbox 4 | # 5 | name: Code_063 6 | type: parsed 7 | protocol: RC5 8 | address: 00 00 00 00 9 | command: 3F 00 00 00 10 | # 11 | name: Code_062 12 | type: parsed 13 | protocol: RC5 14 | address: 00 00 00 00 15 | command: 3E 00 00 00 16 | # 17 | name: Code_061 18 | type: parsed 19 | protocol: RC5 20 | address: 00 00 00 00 21 | command: 3D 00 00 00 22 | # 23 | name: Code_060 24 | type: parsed 25 | protocol: RC5 26 | address: 00 00 00 00 27 | command: 3C 00 00 00 28 | # 29 | name: Code_059 30 | type: parsed 31 | protocol: RC5 32 | address: 00 00 00 00 33 | command: 3B 00 00 00 34 | # 35 | name: Code_058 36 | type: parsed 37 | protocol: RC5 38 | address: 00 00 00 00 39 | command: 3A 00 00 00 40 | # 41 | name: Code_057 42 | type: parsed 43 | protocol: RC5 44 | address: 00 00 00 00 45 | command: 39 00 00 00 46 | # 47 | name: Code_056 48 | type: parsed 49 | protocol: RC5 50 | address: 00 00 00 00 51 | command: 38 00 00 00 52 | # 53 | name: Code_055 54 | type: parsed 55 | protocol: RC5 56 | address: 00 00 00 00 57 | command: 37 00 00 00 58 | # 59 | name: Code_054 60 | type: parsed 61 | protocol: RC5 62 | address: 00 00 00 00 63 | command: 36 00 00 00 64 | # 65 | name: Code_053 66 | type: parsed 67 | protocol: RC5 68 | address: 00 00 00 00 69 | command: 35 00 00 00 70 | # 71 | name: Code_052 72 | type: parsed 73 | protocol: RC5 74 | address: 00 00 00 00 75 | command: 34 00 00 00 76 | # 77 | name: Code_051 78 | type: parsed 79 | protocol: RC5 80 | address: 00 00 00 00 81 | command: 33 00 00 00 82 | # 83 | name: Code_050 84 | type: parsed 85 | protocol: RC5 86 | address: 00 00 00 00 87 | command: 32 00 00 00 88 | # 89 | name: Code_049 90 | type: parsed 91 | protocol: RC5 92 | address: 00 00 00 00 93 | command: 31 00 00 00 94 | # 95 | name: Code_048 96 | type: parsed 97 | protocol: RC5 98 | address: 00 00 00 00 99 | command: 30 00 00 00 100 | # 101 | name: Code_047 102 | type: parsed 103 | protocol: RC5 104 | address: 00 00 00 00 105 | command: 2F 00 00 00 106 | # 107 | name: Code_046 108 | type: parsed 109 | protocol: RC5 110 | address: 00 00 00 00 111 | command: 2E 00 00 00 112 | # 113 | name: Code_045 114 | type: parsed 115 | protocol: RC5 116 | address: 00 00 00 00 117 | command: 2D 00 00 00 118 | # 119 | name: Code_044 120 | type: parsed 121 | protocol: RC5 122 | address: 00 00 00 00 123 | command: 2C 00 00 00 124 | # 125 | name: Code_043 126 | type: parsed 127 | protocol: RC5 128 | address: 00 00 00 00 129 | command: 2B 00 00 00 130 | # 131 | name: Code_042 132 | type: parsed 133 | protocol: RC5 134 | address: 00 00 00 00 135 | command: 2A 00 00 00 136 | # 137 | name: Code_041 138 | type: parsed 139 | protocol: RC5 140 | address: 00 00 00 00 141 | command: 29 00 00 00 142 | # 143 | name: Code_040 144 | type: parsed 145 | protocol: RC5 146 | address: 00 00 00 00 147 | command: 28 00 00 00 148 | # 149 | name: Code_039 150 | type: parsed 151 | protocol: RC5 152 | address: 00 00 00 00 153 | command: 27 00 00 00 154 | # 155 | name: Code_038 156 | type: parsed 157 | protocol: RC5 158 | address: 00 00 00 00 159 | command: 26 00 00 00 160 | # 161 | name: Code_037 162 | type: parsed 163 | protocol: RC5 164 | address: 00 00 00 00 165 | command: 25 00 00 00 166 | # 167 | name: Code_036 168 | type: parsed 169 | protocol: RC5 170 | address: 00 00 00 00 171 | command: 24 00 00 00 172 | # 173 | name: Code_035 174 | type: parsed 175 | protocol: RC5 176 | address: 00 00 00 00 177 | command: 23 00 00 00 178 | # 179 | name: Code_034 180 | type: parsed 181 | protocol: RC5 182 | address: 00 00 00 00 183 | command: 22 00 00 00 184 | # 185 | name: Code_033 186 | type: parsed 187 | protocol: RC5 188 | address: 00 00 00 00 189 | command: 21 00 00 00 190 | # 191 | name: Code_032 192 | type: parsed 193 | protocol: RC5 194 | address: 00 00 00 00 195 | command: 20 00 00 00 196 | # 197 | name: Code_031 198 | type: parsed 199 | protocol: RC5 200 | address: 00 00 00 00 201 | command: 1F 00 00 00 202 | # 203 | name: Code_030 204 | type: parsed 205 | protocol: RC5 206 | address: 00 00 00 00 207 | command: 1E 00 00 00 208 | # 209 | name: Code_029 210 | type: parsed 211 | protocol: RC5 212 | address: 00 00 00 00 213 | command: 1D 00 00 00 214 | # 215 | name: Code_028 216 | type: parsed 217 | protocol: RC5 218 | address: 00 00 00 00 219 | command: 1C 00 00 00 220 | # 221 | name: Code_027 222 | type: parsed 223 | protocol: RC5 224 | address: 00 00 00 00 225 | command: 1B 00 00 00 226 | # 227 | name: Code_026 228 | type: parsed 229 | protocol: RC5 230 | address: 00 00 00 00 231 | command: 1A 00 00 00 232 | # 233 | name: Code_025 234 | type: parsed 235 | protocol: RC5 236 | address: 00 00 00 00 237 | command: 19 00 00 00 238 | # 239 | name: Code_024 240 | type: parsed 241 | protocol: RC5 242 | address: 00 00 00 00 243 | command: 18 00 00 00 244 | # 245 | name: Code_023 246 | type: parsed 247 | protocol: RC5 248 | address: 00 00 00 00 249 | command: 17 00 00 00 250 | # 251 | name: Code_022 252 | type: parsed 253 | protocol: RC5 254 | address: 00 00 00 00 255 | command: 16 00 00 00 256 | # 257 | name: Code_021 258 | type: parsed 259 | protocol: RC5 260 | address: 00 00 00 00 261 | command: 15 00 00 00 262 | # 263 | name: Code_020 264 | type: parsed 265 | protocol: RC5 266 | address: 00 00 00 00 267 | command: 14 00 00 00 268 | # 269 | name: Code_019 270 | type: parsed 271 | protocol: RC5 272 | address: 00 00 00 00 273 | command: 13 00 00 00 274 | # 275 | name: Code_018 276 | type: parsed 277 | protocol: RC5 278 | address: 00 00 00 00 279 | command: 12 00 00 00 280 | # 281 | name: Code_017 282 | type: parsed 283 | protocol: RC5 284 | address: 00 00 00 00 285 | command: 11 00 00 00 286 | # 287 | name: Code_016 288 | type: parsed 289 | protocol: RC5 290 | address: 00 00 00 00 291 | command: 10 00 00 00 292 | # 293 | name: Code_015 294 | type: parsed 295 | protocol: RC5 296 | address: 00 00 00 00 297 | command: 0F 00 00 00 298 | # 299 | name: Code_014 300 | type: parsed 301 | protocol: RC5 302 | address: 00 00 00 00 303 | command: 0E 00 00 00 304 | # 305 | name: Code_013 306 | type: parsed 307 | protocol: RC5 308 | address: 00 00 00 00 309 | command: 0D 00 00 00 310 | # 311 | name: Code_012 312 | type: parsed 313 | protocol: RC5 314 | address: 00 00 00 00 315 | command: 0C 00 00 00 316 | # 317 | name: Code_011 318 | type: parsed 319 | protocol: RC5 320 | address: 00 00 00 00 321 | command: 0B 00 00 00 322 | # 323 | name: Code_010 324 | type: parsed 325 | protocol: RC5 326 | address: 00 00 00 00 327 | command: 0A 00 00 00 328 | # 329 | name: Code_009 330 | type: parsed 331 | protocol: RC5 332 | address: 00 00 00 00 333 | command: 09 00 00 00 334 | # 335 | name: Code_008 336 | type: parsed 337 | protocol: RC5 338 | address: 00 00 00 00 339 | command: 08 00 00 00 340 | # 341 | name: Code_007 342 | type: parsed 343 | protocol: RC5 344 | address: 00 00 00 00 345 | command: 07 00 00 00 346 | # 347 | name: Code_006 348 | type: parsed 349 | protocol: RC5 350 | address: 00 00 00 00 351 | command: 06 00 00 00 352 | # 353 | name: Code_005 354 | type: parsed 355 | protocol: RC5 356 | address: 00 00 00 00 357 | command: 05 00 00 00 358 | # 359 | name: Code_004 360 | type: parsed 361 | protocol: RC5 362 | address: 00 00 00 00 363 | command: 04 00 00 00 364 | # 365 | name: Code_003 366 | type: parsed 367 | protocol: RC5 368 | address: 00 00 00 00 369 | command: 03 00 00 00 370 | # 371 | name: Code_002 372 | type: parsed 373 | protocol: RC5 374 | address: 00 00 00 00 375 | command: 02 00 00 00 376 | # 377 | name: Code_001 378 | type: parsed 379 | protocol: RC5 380 | address: 00 00 00 00 381 | command: 01 00 00 00 382 | # 383 | name: Code_000 384 | type: parsed 385 | protocol: RC5 386 | address: 00 00 00 00 387 | command: 00 00 00 00 388 | -------------------------------------------------------------------------------- /IR/All-Codes/IR-RC5-03-00.ir: -------------------------------------------------------------------------------- 1 | Filetype: IR signals file 2 | Version: 1 3 | # generated with flipper_toolbox 4 | # 5 | name: Code_063 6 | type: parsed 7 | protocol: RC5 8 | address: 03 00 00 00 9 | command: 3F 00 00 00 10 | # 11 | name: Code_062 12 | type: parsed 13 | protocol: RC5 14 | address: 03 00 00 00 15 | command: 3E 00 00 00 16 | # 17 | name: Code_061 18 | type: parsed 19 | protocol: RC5 20 | address: 03 00 00 00 21 | command: 3D 00 00 00 22 | # 23 | name: Code_060 24 | type: parsed 25 | protocol: RC5 26 | address: 03 00 00 00 27 | command: 3C 00 00 00 28 | # 29 | name: Code_059 30 | type: parsed 31 | protocol: RC5 32 | address: 03 00 00 00 33 | command: 3B 00 00 00 34 | # 35 | name: Code_058 36 | type: parsed 37 | protocol: RC5 38 | address: 03 00 00 00 39 | command: 3A 00 00 00 40 | # 41 | name: Code_057 42 | type: parsed 43 | protocol: RC5 44 | address: 03 00 00 00 45 | command: 39 00 00 00 46 | # 47 | name: Code_056 48 | type: parsed 49 | protocol: RC5 50 | address: 03 00 00 00 51 | command: 38 00 00 00 52 | # 53 | name: Code_055 54 | type: parsed 55 | protocol: RC5 56 | address: 03 00 00 00 57 | command: 37 00 00 00 58 | # 59 | name: Code_054 60 | type: parsed 61 | protocol: RC5 62 | address: 03 00 00 00 63 | command: 36 00 00 00 64 | # 65 | name: Code_053 66 | type: parsed 67 | protocol: RC5 68 | address: 03 00 00 00 69 | command: 35 00 00 00 70 | # 71 | name: Code_052 72 | type: parsed 73 | protocol: RC5 74 | address: 03 00 00 00 75 | command: 34 00 00 00 76 | # 77 | name: Code_051 78 | type: parsed 79 | protocol: RC5 80 | address: 03 00 00 00 81 | command: 33 00 00 00 82 | # 83 | name: Code_050 84 | type: parsed 85 | protocol: RC5 86 | address: 03 00 00 00 87 | command: 32 00 00 00 88 | # 89 | name: Code_049 90 | type: parsed 91 | protocol: RC5 92 | address: 03 00 00 00 93 | command: 31 00 00 00 94 | # 95 | name: Code_048 96 | type: parsed 97 | protocol: RC5 98 | address: 03 00 00 00 99 | command: 30 00 00 00 100 | # 101 | name: Code_047 102 | type: parsed 103 | protocol: RC5 104 | address: 03 00 00 00 105 | command: 2F 00 00 00 106 | # 107 | name: Code_046 108 | type: parsed 109 | protocol: RC5 110 | address: 03 00 00 00 111 | command: 2E 00 00 00 112 | # 113 | name: Code_045 114 | type: parsed 115 | protocol: RC5 116 | address: 03 00 00 00 117 | command: 2D 00 00 00 118 | # 119 | name: Code_044 120 | type: parsed 121 | protocol: RC5 122 | address: 03 00 00 00 123 | command: 2C 00 00 00 124 | # 125 | name: Code_043 126 | type: parsed 127 | protocol: RC5 128 | address: 03 00 00 00 129 | command: 2B 00 00 00 130 | # 131 | name: Code_042 132 | type: parsed 133 | protocol: RC5 134 | address: 03 00 00 00 135 | command: 2A 00 00 00 136 | # 137 | name: Code_041 138 | type: parsed 139 | protocol: RC5 140 | address: 03 00 00 00 141 | command: 29 00 00 00 142 | # 143 | name: Code_040 144 | type: parsed 145 | protocol: RC5 146 | address: 03 00 00 00 147 | command: 28 00 00 00 148 | # 149 | name: Code_039 150 | type: parsed 151 | protocol: RC5 152 | address: 03 00 00 00 153 | command: 27 00 00 00 154 | # 155 | name: Code_038 156 | type: parsed 157 | protocol: RC5 158 | address: 03 00 00 00 159 | command: 26 00 00 00 160 | # 161 | name: Code_037 162 | type: parsed 163 | protocol: RC5 164 | address: 03 00 00 00 165 | command: 25 00 00 00 166 | # 167 | name: Code_036 168 | type: parsed 169 | protocol: RC5 170 | address: 03 00 00 00 171 | command: 24 00 00 00 172 | # 173 | name: Code_035 174 | type: parsed 175 | protocol: RC5 176 | address: 03 00 00 00 177 | command: 23 00 00 00 178 | # 179 | name: Code_034 180 | type: parsed 181 | protocol: RC5 182 | address: 03 00 00 00 183 | command: 22 00 00 00 184 | # 185 | name: Code_033 186 | type: parsed 187 | protocol: RC5 188 | address: 03 00 00 00 189 | command: 21 00 00 00 190 | # 191 | name: Code_032 192 | type: parsed 193 | protocol: RC5 194 | address: 03 00 00 00 195 | command: 20 00 00 00 196 | # 197 | name: Code_031 198 | type: parsed 199 | protocol: RC5 200 | address: 03 00 00 00 201 | command: 1F 00 00 00 202 | # 203 | name: Code_030 204 | type: parsed 205 | protocol: RC5 206 | address: 03 00 00 00 207 | command: 1E 00 00 00 208 | # 209 | name: Code_029 210 | type: parsed 211 | protocol: RC5 212 | address: 03 00 00 00 213 | command: 1D 00 00 00 214 | # 215 | name: Code_028 216 | type: parsed 217 | protocol: RC5 218 | address: 03 00 00 00 219 | command: 1C 00 00 00 220 | # 221 | name: Code_027 222 | type: parsed 223 | protocol: RC5 224 | address: 03 00 00 00 225 | command: 1B 00 00 00 226 | # 227 | name: Code_026 228 | type: parsed 229 | protocol: RC5 230 | address: 03 00 00 00 231 | command: 1A 00 00 00 232 | # 233 | name: Code_025 234 | type: parsed 235 | protocol: RC5 236 | address: 03 00 00 00 237 | command: 19 00 00 00 238 | # 239 | name: Code_024 240 | type: parsed 241 | protocol: RC5 242 | address: 03 00 00 00 243 | command: 18 00 00 00 244 | # 245 | name: Code_023 246 | type: parsed 247 | protocol: RC5 248 | address: 03 00 00 00 249 | command: 17 00 00 00 250 | # 251 | name: Code_022 252 | type: parsed 253 | protocol: RC5 254 | address: 03 00 00 00 255 | command: 16 00 00 00 256 | # 257 | name: Code_021 258 | type: parsed 259 | protocol: RC5 260 | address: 03 00 00 00 261 | command: 15 00 00 00 262 | # 263 | name: Code_020 264 | type: parsed 265 | protocol: RC5 266 | address: 03 00 00 00 267 | command: 14 00 00 00 268 | # 269 | name: Code_019 270 | type: parsed 271 | protocol: RC5 272 | address: 03 00 00 00 273 | command: 13 00 00 00 274 | # 275 | name: Code_018 276 | type: parsed 277 | protocol: RC5 278 | address: 03 00 00 00 279 | command: 12 00 00 00 280 | # 281 | name: Code_017 282 | type: parsed 283 | protocol: RC5 284 | address: 03 00 00 00 285 | command: 11 00 00 00 286 | # 287 | name: Code_016 288 | type: parsed 289 | protocol: RC5 290 | address: 03 00 00 00 291 | command: 10 00 00 00 292 | # 293 | name: Code_015 294 | type: parsed 295 | protocol: RC5 296 | address: 03 00 00 00 297 | command: 0F 00 00 00 298 | # 299 | name: Code_014 300 | type: parsed 301 | protocol: RC5 302 | address: 03 00 00 00 303 | command: 0E 00 00 00 304 | # 305 | name: Code_013 306 | type: parsed 307 | protocol: RC5 308 | address: 03 00 00 00 309 | command: 0D 00 00 00 310 | # 311 | name: Code_012 312 | type: parsed 313 | protocol: RC5 314 | address: 03 00 00 00 315 | command: 0C 00 00 00 316 | # 317 | name: Code_011 318 | type: parsed 319 | protocol: RC5 320 | address: 03 00 00 00 321 | command: 0B 00 00 00 322 | # 323 | name: Code_010 324 | type: parsed 325 | protocol: RC5 326 | address: 03 00 00 00 327 | command: 0A 00 00 00 328 | # 329 | name: Code_009 330 | type: parsed 331 | protocol: RC5 332 | address: 03 00 00 00 333 | command: 09 00 00 00 334 | # 335 | name: Code_008 336 | type: parsed 337 | protocol: RC5 338 | address: 03 00 00 00 339 | command: 08 00 00 00 340 | # 341 | name: Code_007 342 | type: parsed 343 | protocol: RC5 344 | address: 03 00 00 00 345 | command: 07 00 00 00 346 | # 347 | name: Code_006 348 | type: parsed 349 | protocol: RC5 350 | address: 03 00 00 00 351 | command: 06 00 00 00 352 | # 353 | name: Code_005 354 | type: parsed 355 | protocol: RC5 356 | address: 03 00 00 00 357 | command: 05 00 00 00 358 | # 359 | name: Code_004 360 | type: parsed 361 | protocol: RC5 362 | address: 03 00 00 00 363 | command: 04 00 00 00 364 | # 365 | name: Code_003 366 | type: parsed 367 | protocol: RC5 368 | address: 03 00 00 00 369 | command: 03 00 00 00 370 | # 371 | name: Code_002 372 | type: parsed 373 | protocol: RC5 374 | address: 03 00 00 00 375 | command: 02 00 00 00 376 | # 377 | name: Code_001 378 | type: parsed 379 | protocol: RC5 380 | address: 03 00 00 00 381 | command: 01 00 00 00 382 | # 383 | name: Code_000 384 | type: parsed 385 | protocol: RC5 386 | address: 03 00 00 00 387 | command: 00 00 00 00 388 | -------------------------------------------------------------------------------- /IR/All-Codes/IR-RC5-30-00.ir: -------------------------------------------------------------------------------- 1 | Filetype: IR signals file 2 | Version: 1 3 | # generated with flipper_toolbox 4 | # 5 | name: Code_063 6 | type: parsed 7 | protocol: RC5 8 | address: 30 00 00 00 9 | command: 3F 00 00 00 10 | # 11 | name: Code_062 12 | type: parsed 13 | protocol: RC5 14 | address: 30 00 00 00 15 | command: 3E 00 00 00 16 | # 17 | name: Code_061 18 | type: parsed 19 | protocol: RC5 20 | address: 30 00 00 00 21 | command: 3D 00 00 00 22 | # 23 | name: Code_060 24 | type: parsed 25 | protocol: RC5 26 | address: 30 00 00 00 27 | command: 3C 00 00 00 28 | # 29 | name: Code_059 30 | type: parsed 31 | protocol: RC5 32 | address: 30 00 00 00 33 | command: 3B 00 00 00 34 | # 35 | name: Code_058 36 | type: parsed 37 | protocol: RC5 38 | address: 30 00 00 00 39 | command: 3A 00 00 00 40 | # 41 | name: Code_057 42 | type: parsed 43 | protocol: RC5 44 | address: 30 00 00 00 45 | command: 39 00 00 00 46 | # 47 | name: Code_056 48 | type: parsed 49 | protocol: RC5 50 | address: 30 00 00 00 51 | command: 38 00 00 00 52 | # 53 | name: Code_055 54 | type: parsed 55 | protocol: RC5 56 | address: 30 00 00 00 57 | command: 37 00 00 00 58 | # 59 | name: Code_054 60 | type: parsed 61 | protocol: RC5 62 | address: 30 00 00 00 63 | command: 36 00 00 00 64 | # 65 | name: Code_053 66 | type: parsed 67 | protocol: RC5 68 | address: 30 00 00 00 69 | command: 35 00 00 00 70 | # 71 | name: Code_052 72 | type: parsed 73 | protocol: RC5 74 | address: 30 00 00 00 75 | command: 34 00 00 00 76 | # 77 | name: Code_051 78 | type: parsed 79 | protocol: RC5 80 | address: 30 00 00 00 81 | command: 33 00 00 00 82 | # 83 | name: Code_050 84 | type: parsed 85 | protocol: RC5 86 | address: 30 00 00 00 87 | command: 32 00 00 00 88 | # 89 | name: Code_049 90 | type: parsed 91 | protocol: RC5 92 | address: 30 00 00 00 93 | command: 31 00 00 00 94 | # 95 | name: Code_048 96 | type: parsed 97 | protocol: RC5 98 | address: 30 00 00 00 99 | command: 30 00 00 00 100 | # 101 | name: Code_047 102 | type: parsed 103 | protocol: RC5 104 | address: 30 00 00 00 105 | command: 2F 00 00 00 106 | # 107 | name: Code_046 108 | type: parsed 109 | protocol: RC5 110 | address: 30 00 00 00 111 | command: 2E 00 00 00 112 | # 113 | name: Code_045 114 | type: parsed 115 | protocol: RC5 116 | address: 30 00 00 00 117 | command: 2D 00 00 00 118 | # 119 | name: Code_044 120 | type: parsed 121 | protocol: RC5 122 | address: 30 00 00 00 123 | command: 2C 00 00 00 124 | # 125 | name: Code_043 126 | type: parsed 127 | protocol: RC5 128 | address: 30 00 00 00 129 | command: 2B 00 00 00 130 | # 131 | name: Code_042 132 | type: parsed 133 | protocol: RC5 134 | address: 30 00 00 00 135 | command: 2A 00 00 00 136 | # 137 | name: Code_041 138 | type: parsed 139 | protocol: RC5 140 | address: 30 00 00 00 141 | command: 29 00 00 00 142 | # 143 | name: Code_040 144 | type: parsed 145 | protocol: RC5 146 | address: 30 00 00 00 147 | command: 28 00 00 00 148 | # 149 | name: Code_039 150 | type: parsed 151 | protocol: RC5 152 | address: 30 00 00 00 153 | command: 27 00 00 00 154 | # 155 | name: Code_038 156 | type: parsed 157 | protocol: RC5 158 | address: 30 00 00 00 159 | command: 26 00 00 00 160 | # 161 | name: Code_037 162 | type: parsed 163 | protocol: RC5 164 | address: 30 00 00 00 165 | command: 25 00 00 00 166 | # 167 | name: Code_036 168 | type: parsed 169 | protocol: RC5 170 | address: 30 00 00 00 171 | command: 24 00 00 00 172 | # 173 | name: Code_035 174 | type: parsed 175 | protocol: RC5 176 | address: 30 00 00 00 177 | command: 23 00 00 00 178 | # 179 | name: Code_034 180 | type: parsed 181 | protocol: RC5 182 | address: 30 00 00 00 183 | command: 22 00 00 00 184 | # 185 | name: Code_033 186 | type: parsed 187 | protocol: RC5 188 | address: 30 00 00 00 189 | command: 21 00 00 00 190 | # 191 | name: Code_032 192 | type: parsed 193 | protocol: RC5 194 | address: 30 00 00 00 195 | command: 20 00 00 00 196 | # 197 | name: Code_031 198 | type: parsed 199 | protocol: RC5 200 | address: 30 00 00 00 201 | command: 1F 00 00 00 202 | # 203 | name: Code_030 204 | type: parsed 205 | protocol: RC5 206 | address: 30 00 00 00 207 | command: 1E 00 00 00 208 | # 209 | name: Code_029 210 | type: parsed 211 | protocol: RC5 212 | address: 30 00 00 00 213 | command: 1D 00 00 00 214 | # 215 | name: Code_028 216 | type: parsed 217 | protocol: RC5 218 | address: 30 00 00 00 219 | command: 1C 00 00 00 220 | # 221 | name: Code_027 222 | type: parsed 223 | protocol: RC5 224 | address: 30 00 00 00 225 | command: 1B 00 00 00 226 | # 227 | name: Code_026 228 | type: parsed 229 | protocol: RC5 230 | address: 30 00 00 00 231 | command: 1A 00 00 00 232 | # 233 | name: Code_025 234 | type: parsed 235 | protocol: RC5 236 | address: 30 00 00 00 237 | command: 19 00 00 00 238 | # 239 | name: Code_024 240 | type: parsed 241 | protocol: RC5 242 | address: 30 00 00 00 243 | command: 18 00 00 00 244 | # 245 | name: Code_023 246 | type: parsed 247 | protocol: RC5 248 | address: 30 00 00 00 249 | command: 17 00 00 00 250 | # 251 | name: Code_022 252 | type: parsed 253 | protocol: RC5 254 | address: 30 00 00 00 255 | command: 16 00 00 00 256 | # 257 | name: Code_021 258 | type: parsed 259 | protocol: RC5 260 | address: 30 00 00 00 261 | command: 15 00 00 00 262 | # 263 | name: Code_020 264 | type: parsed 265 | protocol: RC5 266 | address: 30 00 00 00 267 | command: 14 00 00 00 268 | # 269 | name: Code_019 270 | type: parsed 271 | protocol: RC5 272 | address: 30 00 00 00 273 | command: 13 00 00 00 274 | # 275 | name: Code_018 276 | type: parsed 277 | protocol: RC5 278 | address: 30 00 00 00 279 | command: 12 00 00 00 280 | # 281 | name: Code_017 282 | type: parsed 283 | protocol: RC5 284 | address: 30 00 00 00 285 | command: 11 00 00 00 286 | # 287 | name: Code_016 288 | type: parsed 289 | protocol: RC5 290 | address: 30 00 00 00 291 | command: 10 00 00 00 292 | # 293 | name: Code_015 294 | type: parsed 295 | protocol: RC5 296 | address: 30 00 00 00 297 | command: 0F 00 00 00 298 | # 299 | name: Code_014 300 | type: parsed 301 | protocol: RC5 302 | address: 30 00 00 00 303 | command: 0E 00 00 00 304 | # 305 | name: Code_013 306 | type: parsed 307 | protocol: RC5 308 | address: 30 00 00 00 309 | command: 0D 00 00 00 310 | # 311 | name: Code_012 312 | type: parsed 313 | protocol: RC5 314 | address: 30 00 00 00 315 | command: 0C 00 00 00 316 | # 317 | name: Code_011 318 | type: parsed 319 | protocol: RC5 320 | address: 30 00 00 00 321 | command: 0B 00 00 00 322 | # 323 | name: Code_010 324 | type: parsed 325 | protocol: RC5 326 | address: 30 00 00 00 327 | command: 0A 00 00 00 328 | # 329 | name: Code_009 330 | type: parsed 331 | protocol: RC5 332 | address: 30 00 00 00 333 | command: 09 00 00 00 334 | # 335 | name: Code_008 336 | type: parsed 337 | protocol: RC5 338 | address: 30 00 00 00 339 | command: 08 00 00 00 340 | # 341 | name: Code_007 342 | type: parsed 343 | protocol: RC5 344 | address: 30 00 00 00 345 | command: 07 00 00 00 346 | # 347 | name: Code_006 348 | type: parsed 349 | protocol: RC5 350 | address: 30 00 00 00 351 | command: 06 00 00 00 352 | # 353 | name: Code_005 354 | type: parsed 355 | protocol: RC5 356 | address: 30 00 00 00 357 | command: 05 00 00 00 358 | # 359 | name: Code_004 360 | type: parsed 361 | protocol: RC5 362 | address: 30 00 00 00 363 | command: 04 00 00 00 364 | # 365 | name: Code_003 366 | type: parsed 367 | protocol: RC5 368 | address: 30 00 00 00 369 | command: 03 00 00 00 370 | # 371 | name: Code_002 372 | type: parsed 373 | protocol: RC5 374 | address: 30 00 00 00 375 | command: 02 00 00 00 376 | # 377 | name: Code_001 378 | type: parsed 379 | protocol: RC5 380 | address: 30 00 00 00 381 | command: 01 00 00 00 382 | # 383 | name: Code_000 384 | type: parsed 385 | protocol: RC5 386 | address: 30 00 00 00 387 | command: 00 00 00 00 388 | -------------------------------------------------------------------------------- /IR/All-Codes/README.md: -------------------------------------------------------------------------------- 1 | # Flipper IR Signals Files # 2 | 3 | 4 | 5 | A Collection of Most (All) possible commands for IR common protocol IR "addresses" 6 | 7 | Good for discovering hidden menus 8 | 9 | generated using [gen_all_ir_codes.py](../../ir_gen_all_codes.py) 10 | 11 | 12 | Addresses chosen from scanning [Flipper-IRDB](https://github.com/Lucaslhm/Flipper-IRDB) 13 | 14 | --- 15 | 16 | 17 | #### [NEC](https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol) #### 18 | 19 |     **[IR-NEC-00-00.ir](IR-NEC-00-00.ir)** All command codes for protocol [NEC](https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol), address 00 00 00 00 20 | 21 |     **[IR-NEC-04-00.ir](IR-NEC-04-00.ir)** NEC, address 04 00 00 00 22 | 23 |     **[IR-NEC-40-00.ir](IR-NEC-40-00.ir)** NEC, address 40 00 00 00 24 | 25 |     **[IR-NEC-50-00.ir](IR-NEC-50-00.ir)** NEC, address 50 00 00 00 26 | 27 | #### [Extended NEC protocol (NECext)](https://www.sbprojects.net/knowledge/ir/nec.php#:~:text=Extended+NEC+protocol) 28 | 29 |     **[IR-NECext-00-00.ir](IR-NECext-00-00.ir)** NECext, address 00 00 00 00 30 | 31 |     **[IR-NECext-00-7F.ir](IR-NECext-00-7F.ir)** NECext, address 00 7F 00 00 32 | 33 |     **[IR-NECext-02-00.ir](IR-NECext-02-00.ir)** NECext, address 02 00 00 00 34 | 35 |     **[IR-NECext-84-00.ir](IR-NECext-84-00.ir)** NECext, address 84 00 00 00 36 | 37 |     **[IR-NECext-85-00.ir](IR-NECext-85-00.ir)** NECext, address 85 00 00 00 38 | 39 |     **[IR-NECext-86-00.ir](IR-NECext-86-00.ir)** NECext, address 86 00 00 00 40 | 41 |     **[IR-NECext-EA-00.ir](IR-NECext-EA-00.ir)** NECext, address EA 00 00 00 42 | 43 | #### [RC5](https://www.mikrocontroller.net/articles/IRMP_-_english#RC5_+_RC5X) #### 44 | 45 |     **[IR-RC5-00-00.ir](IR-RC5-00-00.ir)** RC5, address 00 00 00 00 46 | 47 |     **[IR-RC5-03-00.ir](IR-RC5-03-00.ir)** RC5, address 03 00 00 00 48 | 49 |     **[IR-RC5-30-00.ir](IR-RC5-30-00.ir)** RC5, address 30 00 00 00 50 | 51 | #### [RC6](https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_+_RC6A) #### 52 | 53 |     **[IR-RC6-00-00.ir](IR-RC6-00-00.ir)** RC6, address 00 00 00 00 54 | 55 | #### [Sony SIRC](https://www.sbprojects.net/knowledge/ir/sirc.php) #### 56 | 57 |     **[IR-SIRC-01-00.ir](IR-SIRC-01-00.ir)** Sony SIRC protocol address 01 00 00 00 58 | 59 |     **[IR-SIRC-10-00.ir](IR-SIRC-10-00.ir)** Sony SIRC protocol address 10 00 00 00 60 | 61 | #### [Sony SIRC15](https://www.sbprojects.net/knowledge/ir/sirc.php) #### 62 | 63 |     **[IR-SIRC15-1A-00.ir](IR-SIRC15-1A-00.ir)** Sony SIRC15 protocol address 1A 00 00 00 64 | 65 |     **[IR-SIRC15-77-00.ir](IR-SIRC15-77-00.ir)** Sony SIRC15 protocol address 77 00 00 00 66 | 67 |     **[IR-SIRC15-94-00.ir](IR-SIRC15-94-00.ir)** Sony SIRC15 protocol address 94 00 00 00 68 | 69 |     **[IR-SIRC15-A1-00.ir](IR-SIRC15-A1-00.ir)** Sony SIRC15 protocol address A1 00 00 00 70 | 71 | #### [Samsung](https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG32) #### 72 | 73 |     **[IR-Samsung32-00-00.ir](IR-Samsung32-00-00.ir)** protocol address 00 00 00 00 74 | 75 |     **[IR-Samsung32-06-00.ir](IR-Samsung32-06-00.ir)** protocol address 06 00 00 00 76 | 77 |     **[IR-Samsung32-07-00.ir](IR-Samsung32-07-00.ir)** protocol address 07 00 00 00 78 | 79 |     **[IR-Samsung32-08-00.ir](IR-Samsung32-08-00.ir)** protocol address 08 00 00 00 80 | 81 |     **[IR-Samsung32-0B-00.ir](IR-Samsung32-0B-00.ir)** protocol address 0B 00 00 00 82 | 83 |     **[IR-Samsung32-0E-00.ir](IR-Samsung32-0E-00.ir)** protocol address 0E 00 00 00 84 | 85 |     **[IR-Samsung32-37-07.ir](IR-Samsung32-37-07.ir)** protocol address 37 07 00 00 86 | 87 | 88 | -------------------------------------------------------------------------------- /IR/Lego_Remote.ir: -------------------------------------------------------------------------------- 1 | Filetype: IR signals file 2 | Version: 1 3 | # 4 | # LEGO Power Functions Speed Remote Control (8879) 5 | # 6 | name: Left_Button 7 | type: raw 8 | frequency: 38000 9 | duty_cycle: 0.330000 10 | data: 176 1028 174 293 173 241 178 263 177 263 177 265 175 556 180 288 178 237 177 554 176 291 180 234 180 287 179 235 179 263 177 555 176 540 175 100990 179 1025 177 290 176 265 180 260 175 239 175 294 172 532 178 290 181 261 174 530 175 293 173 268 177 263 172 269 176 265 180 526 179 537 178 100854 180 1024 178 262 178 263 177 290 176 264 181 261 174 531 179 262 178 290 176 530 175 266 179 288 178 262 173 268 177 265 180 526 179 536 179 100927 176 1028 179 287 179 262 173 268 177 264 171 244 181 551 174 293 173 269 176 529 176 291 175 266 179 235 179 288 178 264 181 525 179 536 179 100958 177 1027 180 287 179 262 173 241 178 262 178 264 181 550 175 292 174 241 178 553 178 290 176 265 180 234 180 261 174 268 177 555 181 535 180 11 | # 12 | name: Left_Knob_cc 13 | type: raw 14 | frequency: 38000 15 | duty_cycle: 0.330000 16 | data: 181 1024 178 554 176 264 176 264 181 260 175 267 178 554 176 555 176 265 180 262 178 553 178 263 177 264 176 266 179 552 179 263 177 538 177 101158 181 1023 179 553 177 263 177 264 176 264 176 266 179 553 177 554 177 264 181 261 174 557 179 262 178 263 177 265 180 551 174 267 178 537 178 100865 174 1030 177 554 177 265 180 260 175 266 179 262 178 555 176 556 180 261 179 263 177 554 182 260 175 265 180 262 178 553 177 265 180 535 180 101323 181 1024 178 554 176 264 176 264 181 260 175 267 178 554 176 555 176 265 180 262 178 553 177 264 176 264 176 266 179 552 178 264 176 539 176 101293 180 1026 176 555 181 260 175 266 179 261 179 263 177 555 181 551 180 262 178 263 177 555 176 265 180 261 174 267 178 554 176 265 175 540 175 17 | # 18 | name: Left_Knob_cw 19 | type: raw 20 | frequency: 38000 21 | duty_cycle: 0.330000 22 | data: 178 1027 180 551 174 267 178 262 178 263 177 264 181 551 179 552 178 263 177 264 176 556 180 262 178 553 178 265 180 551 174 266 179 245 179 101195 181 1024 178 554 176 264 176 265 180 260 175 267 178 555 181 551 174 266 179 263 177 554 177 266 179 552 179 263 177 555 181 260 175 249 176 101288 177 1027 180 551 179 262 178 262 178 263 177 265 180 552 178 553 177 264 176 265 175 557 178 263 177 555 176 266 179 552 178 263 177 247 178 23 | # 24 | name: Right_Button 25 | type: raw 26 | frequency: 38000 27 | duty_cycle: 0.330000 28 | data: 175 1029 178 262 178 263 177 264 176 264 181 260 175 557 179 263 177 556 175 557 178 262 178 262 178 263 177 264 176 265 180 551 174 251 173 100980 177 1026 176 265 180 260 175 266 179 261 179 263 177 554 182 260 175 558 178 554 176 264 176 264 181 260 175 266 179 262 178 553 177 247 178 100668 177 1026 176 265 180 260 175 266 179 261 179 263 177 554 182 261 174 558 178 554 182 259 176 265 180 261 174 266 179 263 177 554 177 248 177 100916 176 1027 175 265 180 261 174 267 178 262 178 264 176 555 181 261 179 554 177 555 181 260 175 266 179 261 179 262 178 264 176 555 176 249 176 100949 180 1025 177 263 177 264 181 259 176 265 180 261 179 553 178 264 181 552 178 553 177 263 177 264 176 264 181 260 175 267 178 553 177 247 178 29 | # 30 | name: Right_Knob_cc 31 | type: raw 32 | frequency: 38000 33 | duty_cycle: 0.330000 34 | data: 179 1024 178 263 177 263 177 264 176 264 181 261 174 558 178 555 180 551 174 268 177 554 176 264 176 265 180 553 177 554 176 264 181 243 182 123550 177 1028 179 552 178 262 178 263 177 263 177 265 180 552 178 554 177 555 181 261 174 557 179 262 178 263 177 264 181 551 174 266 179 245 179 100820 176 1028 179 553 177 263 177 263 177 264 181 260 175 558 178 555 181 551 174 267 178 553 177 264 176 264 181 261 174 557 178 263 177 247 178 100531 180 1024 178 554 176 264 176 265 180 260 175 267 178 554 181 551 174 557 178 264 176 555 175 265 180 261 174 268 177 554 176 264 176 248 177 100897 177 1027 180 552 178 262 178 262 178 263 177 265 180 552 178 554 177 555 181 261 179 553 177 263 177 263 177 265 180 551 174 267 178 246 178 100898 181 1023 179 553 177 263 177 264 181 259 176 266 179 553 177 556 174 557 178 263 177 554 177 265 180 260 175 267 178 553 177 263 177 248 177 35 | # 36 | name: Right_Knob_cw 37 | type: raw 38 | frequency: 38000 39 | duty_cycle: 0.330000 40 | data: 178 1025 177 264 176 265 180 260 175 266 179 263 177 555 175 557 178 553 177 265 175 556 179 262 178 554 176 556 179 552 178 264 176 539 176 129988 178 1026 181 550 175 266 179 261 179 262 178 263 177 556 174 558 177 554 176 265 175 556 179 262 178 554 176 265 180 551 174 268 177 538 177 101186 175 1030 177 554 176 264 181 260 175 265 180 262 178 554 176 556 179 552 178 263 177 555 180 261 174 557 178 264 181 550 175 293 173 515 179 100900 179 1025 177 554 181 259 176 265 180 261 174 267 178 555 180 552 178 553 177 264 181 551 174 267 178 554 176 265 175 556 179 263 177 538 177 100874 180 1025 177 554 181 260 175 265 180 261 174 268 177 555 180 552 178 553 177 265 180 551 174 268 177 554 176 265 175 557 178 263 177 538 177 101270 178 1027 175 556 179 262 178 262 178 263 177 264 176 557 178 554 181 550 175 267 178 553 177 264 176 556 179 262 178 554 176 265 180 535 180 41 | -------------------------------------------------------------------------------- /IR/Lego_Train.ir: -------------------------------------------------------------------------------- 1 | Filetype: IR signals file 2 | Version: 1 3 | # 4 | # IR Remote for Lego Train 5 | # 6 | name: Stop 7 | type: raw 8 | frequency: 38000 9 | duty_cycle: 0.330000 10 | data: 175 1029 178 263 177 263 177 264 176 265 180 262 178 553 177 264 181 261 174 557 178 263 177 264 176 265 180 260 175 267 178 555 180 535 179 100783 178 1026 181 260 175 266 179 262 178 262 178 264 181 551 174 266 179 263 177 555 175 265 180 261 179 288 173 242 177 265 175 557 178 537 178 123667 178 1027 180 552 178 288 173 242 177 264 176 265 180 552 178 262 178 290 176 530 174 266 179 288 178 238 176 555 180 262 178 554 176 539 176 101028 201 1004 198 534 201 239 206 235 200 241 204 237 198 534 201 240 205 236 199 533 202 239 206 234 201 241 204 527 198 245 200 532 203 512 203 101108 179 1026 181 551 174 267 178 262 178 263 177 265 180 552 178 263 177 264 181 551 174 267 178 263 177 264 176 556 179 263 177 555 175 541 174 100702 181 1024 178 554 176 291 180 260 175 266 179 263 172 534 181 286 180 263 172 533 182 259 176 292 174 268 177 528 176 265 180 553 177 539 181 100720 178 1026 181 551 174 293 173 242 177 264 176 265 180 578 178 237 177 265 180 577 179 236 178 263 177 265 175 583 173 269 176 556 179 536 178 108292 176 1028 179 262 178 263 177 264 176 265 180 261 179 553 177 263 177 265 175 557 178 289 177 237 177 290 176 239 175 266 179 554 176 539 181 100771 178 1025 177 264 176 264 181 260 175 266 179 263 177 581 180 234 180 262 178 553 177 290 176 265 180 234 180 261 174 268 177 555 180 535 180 100824 178 1025 177 264 176 265 180 287 179 262 173 243 176 555 175 292 174 268 177 528 176 264 181 260 175 293 173 268 177 264 181 525 179 536 179 100885 174 1030 177 263 177 291 175 266 179 262 173 269 176 528 176 291 175 241 178 580 171 270 175 266 179 261 174 267 178 264 181 525 179 536 179 101295 179 1024 178 263 177 264 176 291 175 266 179 237 177 554 181 286 180 236 178 553 177 264 176 265 180 287 179 262 173 243 181 551 174 541 179 11 | # 12 | name: Forward 13 | type: raw 14 | frequency: 38000 15 | duty_cycle: 0.330000 16 | data: 178 1027 180 260 175 293 173 241 178 289 172 244 180 552 178 554 176 290 176 240 174 557 178 289 177 265 180 526 178 554 176 265 175 541 174 101227 224 980 175 292 174 240 179 262 178 289 177 238 176 557 178 553 177 290 181 235 179 552 178 289 172 244 180 553 177 554 176 266 179 536 179 123558 181 1025 177 554 181 286 180 235 179 288 178 264 181 525 179 553 177 289 172 244 180 552 178 262 178 263 177 265 175 556 179 263 177 538 176 132149 177 1027 175 293 173 267 178 237 177 290 176 265 180 527 177 554 181 286 180 235 179 553 177 263 177 265 180 553 177 554 176 266 179 536 179 130708 178 1027 180 551 179 262 178 289 177 237 177 265 180 552 178 553 177 265 180 261 179 552 178 263 177 263 177 265 180 551 179 262 178 538 177 100702 178 1027 180 552 178 262 178 263 177 264 176 265 180 553 177 555 175 265 180 289 177 528 176 264 181 260 180 262 178 554 176 292 179 510 174 100725 181 1024 178 554 181 286 180 261 174 267 178 263 172 535 179 552 178 263 177 291 175 530 179 288 173 242 177 291 180 525 179 289 177 512 177 100728 178 1027 180 552 178 262 178 263 177 290 181 261 174 533 176 555 175 265 180 289 177 528 176 264 181 287 179 263 172 533 182 287 179 510 179 100759 180 1026 181 551 179 262 178 263 177 264 176 265 180 553 177 554 176 265 180 262 178 553 177 265 180 260 175 267 178 553 177 265 175 540 180 111768 177 1027 180 261 179 288 173 268 177 264 181 261 174 532 177 554 176 265 180 261 179 553 177 263 177 265 175 558 177 554 181 287 179 510 179 17 | # 18 | name: Reverse 19 | type: raw 20 | frequency: 38000 21 | duty_cycle: 0.330000 22 | data: 180 1024 178 263 177 264 176 264 181 260 175 267 178 555 180 551 179 262 178 264 176 555 175 267 178 555 180 552 178 553 177 264 181 243 181 100931 178 1026 228 213 232 156 279 162 283 158 277 165 280 505 173 558 177 289 177 239 175 557 178 289 177 530 174 558 177 555 180 260 175 250 174 132676 179 1026 181 551 174 293 173 241 178 289 172 244 180 552 178 553 177 264 176 266 174 557 178 290 176 529 175 293 173 532 177 290 181 217 223 131846 178 1025 177 264 181 286 180 261 174 267 178 237 177 556 179 552 178 263 177 291 175 530 174 267 178 555 180 552 178 554 176 290 176 222 182 100698 178 1025 177 264 181 260 175 292 174 267 178 264 181 525 179 553 177 263 177 291 175 531 179 263 177 556 179 553 177 554 176 291 175 224 180 100726 177 1026 181 260 175 292 174 241 178 263 177 291 175 531 178 554 176 290 176 240 174 558 177 290 176 531 178 554 176 556 179 261 179 246 178 101095 177 1027 180 288 178 263 172 269 176 238 176 266 179 553 177 555 175 292 174 268 177 528 176 292 174 532 177 556 174 557 178 289 177 221 177 101100 174 1030 177 264 176 265 180 260 175 266 179 263 177 556 174 557 178 263 177 265 175 557 178 263 177 556 174 558 177 555 180 260 175 250 174 23 | -------------------------------------------------------------------------------- /IR/Minolta_MN674.ir: -------------------------------------------------------------------------------- 1 | Filetype: IR signals file 2 | Version: 1 3 | # 4 | # Minolta MN674 Projector 5 | # 6 | name: Power 7 | type: parsed 8 | protocol: NECext 9 | address: 00 DF 00 00 10 | command: 1C E3 00 00 11 | # 12 | name: Flip 13 | type: parsed 14 | protocol: NECext 15 | address: 00 DF 00 00 16 | command: 08 F7 00 00 17 | # 18 | name: Vol_down 19 | type: parsed 20 | protocol: NECext 21 | address: 00 DF 00 00 22 | command: 4F B0 00 00 23 | # 24 | name: Vol_up 25 | type: parsed 26 | protocol: NECext 27 | address: 00 DF 00 00 28 | command: 4B B4 00 00 29 | # 30 | name: Home 31 | type: parsed 32 | protocol: NECext 33 | address: 00 DF 00 00 34 | command: 03 FC 00 00 35 | # 36 | name: Menu 37 | type: parsed 38 | protocol: NECext 39 | address: 00 DF 00 00 40 | command: 18 E7 00 00 41 | # 42 | name: Back 43 | type: parsed 44 | protocol: NECext 45 | address: 00 DF 00 00 46 | command: 0A F5 00 00 47 | # 48 | name: Ok 49 | type: parsed 50 | protocol: NECext 51 | address: 00 DF 00 00 52 | command: 06 F9 00 00 53 | # 54 | name: Up 55 | type: parsed 56 | protocol: NECext 57 | address: 00 DF 00 00 58 | command: 1A E5 00 00 59 | # 60 | name: Left 61 | type: parsed 62 | protocol: NECext 63 | address: 00 DF 00 00 64 | command: 47 B8 00 00 65 | # 66 | name: Right 67 | type: parsed 68 | protocol: NECext 69 | address: 00 DF 00 00 70 | command: 07 F8 00 00 71 | # 72 | name: Down 73 | type: parsed 74 | protocol: NECext 75 | address: 00 DF 00 00 76 | command: 48 B7 00 00 77 | # 78 | name: 1 79 | type: parsed 80 | protocol: NECext 81 | address: 00 DF 00 00 82 | command: 54 AB 00 00 83 | # 84 | name: 2 85 | type: parsed 86 | protocol: NECext 87 | address: 00 DF 00 00 88 | command: 16 E9 00 00 89 | # 90 | name: 3 91 | type: parsed 92 | protocol: NECext 93 | address: 00 DF 00 00 94 | command: 15 EA 00 00 95 | # 96 | name: 4 97 | type: parsed 98 | protocol: NECext 99 | address: 00 DF 00 00 100 | command: 50 AF 00 00 101 | # 102 | name: 5 103 | type: parsed 104 | protocol: NECext 105 | address: 00 DF 00 00 106 | command: 12 ED 00 00 107 | # 108 | name: 6 109 | type: parsed 110 | protocol: NECext 111 | address: 00 DF 00 00 112 | command: 11 EE 00 00 113 | # 114 | name: 7 115 | type: parsed 116 | protocol: NECext 117 | address: 00 DF 00 00 118 | command: 4C B3 00 00 119 | # 120 | name: 8 121 | type: parsed 122 | protocol: NECext 123 | address: 00 DF 00 00 124 | command: 0E F1 00 00 125 | # 126 | name: 9 127 | type: parsed 128 | protocol: NECext 129 | address: 00 DF 00 00 130 | command: 0D F2 00 00 131 | # 132 | name: 0 133 | type: parsed 134 | protocol: NECext 135 | address: 00 DF 00 00 136 | command: 0C F3 00 00 137 | # 138 | name: Mouse 139 | type: parsed 140 | protocol: NECext 141 | address: 00 DF 00 00 142 | command: 42 BD 00 00 143 | # 144 | name: Del 145 | type: parsed 146 | protocol: NECext 147 | address: 00 DF 00 00 148 | command: 10 EF 00 00 149 | -------------------------------------------------------------------------------- /IR/README.md: -------------------------------------------------------------------------------- 1 | # Flipper IR Signals Files # 2 | 3 | A Collection of IR Signal Files 4 | 5 | --- 6 | 7 | 8 | #### [Minolta_MN674.ir](Minolta_MN674.ir) #### 9 | 10 | Minolta MN674 Projector 11 | 12 | Amazon product link: [Minolta-MN674](https://www.amazon.com/Minolta-MN674/dp/B08WZ3DNL2/) 13 | 14 | --- 15 | 16 | 17 | #### [Roomba_5101IR.ir](Roomba_5101IR.ir) #### 18 | 19 | Roomba 5101IR IRobot Remote Scheduler (older model) 20 | 21 | Amazon product link: [Remote Control 400 or 500 Series](https://www.amazon.com/Ship-Roomba-Scheduler-Remote-Control/dp/B01693B816/) / [Roomba 5101IR IRobot Remote Scheduler](https://www.amazon.com/Roomba-5101IR-IRobot-Remote-Scheduler/dp/B000E7DL9Q) 22 | 23 | 24 | --- 25 | 26 | 27 | #### [Midea_AC_Remote.ir](Midea_AC_Remote.ir) #### 28 | 29 | Midea Air Conditioner Remote Control RG10F2(B2)/BGEFU1 30 | 31 | Product link [Replacement Remote Midea Air Conditioner](https://www.amazon.com/Replace-Control-Projector-0rigjnal-Conditioner/dp/B0BZHZ6MZT) 32 | 33 | --- 34 | 35 | 36 | #### [Lego_Remote.ir](Lego_Remote.ir) #### 37 | 38 | LEGO Power Functions Speed Remote Control (8879) 39 | 40 | Product link [Lego Item #8879](https://www.lego.com/en-us/product/lego-power-functions-ir-speed-remote-control-8879) 41 | 42 | --- 43 | 44 | ### [IR/All-Codes](All-Codes) ### 45 | 46 | A Collection of alll All possible commands for IR common protocol IR "addresses" 47 | 48 | Good for discovering hidden menus 49 | 50 | ( generated using [gen_all_ir_codes.py](../ir_gen_all_codes.py) ) 51 | 52 | --- 53 | -------------------------------------------------------------------------------- /IR/Roomba_5101IR.ir: -------------------------------------------------------------------------------- 1 | Filetype: IR signals file 2 | Version: 1 3 | # 4 | # Roomba Remote 5 | # generated with flipper_toolbox 6 | # 7 | name: Power 8 | type: raw 9 | frequency: 38000 10 | duty_cycle: 0.330000 11 | data: 2743 968 992 2923 965 2949 970 2956 2892 985 974 2941 2897 993 967 12 | # 13 | name: Left 14 | type: raw 15 | frequency: 38000 16 | duty_cycle: 0.330000 17 | data: 2742 980 990 2922 966 2947 993 2907 970 2942 998 2902 2925 977 2921 32418 2740 982 998 2915 973 2940 969 2931 967 2945 974 2926 2922 968 2920 32432 2747 976 994 2919 969 2931 999 2914 974 2926 993 2920 2897 992 2917 32436 2712 998 992 2920 968 2945 995 2905 972 2941 999 2901 2926 975 2923 32417 2741 982 998 2915 973 2940 969 2931 967 2933 996 2916 2921 968 2920 18 | # 19 | name: Right 20 | type: raw 21 | frequency: 38000 22 | duty_cycle: 0.330000 23 | data: 2739 983 1018 2895 972 2940 969 2932 966 2946 973 2927 971 2929 2919 32420 2746 977 993 2921 967 2933 996 2917 971 2929 990 2923 965 2935 2923 32418 2738 972 1018 2908 969 2931 998 2902 965 2948 992 2908 969 2944 2893 32435 2742 983 997 2914 974 2939 969 2931 967 2933 996 2917 970 2930 2927 24 | # 25 | name: Forward 26 | type: raw 27 | frequency: 38000 28 | duty_cycle: 0.330000 29 | data: 2739 969 990 2935 973 2927 992 2921 967 2933 996 2917 2899 990 990 34362 2711 999 991 2935 973 2927 992 2908 969 2944 964 2936 2922 980 1000 34340 2744 979 991 2923 975 2925 994 2920 968 2932 997 2916 2900 990 1000 34340 2744 980 990 2923 965 2949 970 2931 967 2946 973 2928 2919 970 1000 34356 2717 995 995 2917 970 2943 965 2935 973 2940 968 2933 2925 967 992 34363 2720 992 998 2914 974 2966 942 2933 965 2935 994 2920 2896 996 994 34349 2745 980 1000 2912 975 2951 968 2920 978 2949 970 2918 2898 994 996 34346 2738 987 993 2919 969 2958 971 2942 946 2930 999 2914 2892 1000 1001 34340 2745 981 999 2912 976 2925 994 2920 968 2933 996 2917 2899 990 990 34350 2744 980 1000 2913 974 2939 969 2931 967 2946 973 2928 2919 970 999 30 | # 31 | name: Spot 32 | type: raw 33 | frequency: 38000 34 | duty_cycle: 0.330000 35 | data: 2743 978 971 2968 941 2972 947 2927 971 2967 2870 992 998 2941 947 34374 2720 989 970 2943 965 2948 971 2929 969 2943 2894 995 995 2930 968 36 | # 37 | name: Clean 38 | type: raw 39 | frequency: 38000 40 | duty_cycle: 0.330000 41 | data: 2717 978 971 2942 966 2933 965 2948 2899 990 1000 2926 972 2928 970 42 | # 43 | name: Max 44 | type: raw 45 | frequency: 38000 46 | duty_cycle: 0.330000 47 | data: 2737 972 1049 2875 971 2929 990 2923 964 2935 2923 981 968 2944 2924 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Peter Shipley 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # make lint : runs pylint on all all sripts (default) 4 | # 5 | # make pep8 : run pycodestyle on all script files 6 | # 7 | # make secplus : download the secplus library 8 | # 9 | # make ndeflib ; install ndeflib (NDEF messages) library 10 | # 11 | # make gen-files : (re)generate ir/subghz/nfc data file. 12 | # 13 | # make clone-repos : doanload other repos with useful flipper data files 14 | # 15 | 16 | 17 | 18 | PYCODESTYLE=pycodestyle 19 | PEP8ARG=--ignore=E501,E221,E241,E502,W503 20 | 21 | PYLINT=pylint 22 | 23 | 24 | FILES=subghz_secplusv1.py subghz_secplusv2.py \ 25 | nfc_prox2flip.py \ 26 | nfc_dict_strip.py nfc_dict_diff.py \ 27 | subghz_ook_to_sub.py subghz_x10.py subghz_insteon.py \ 28 | ir_gen_all_codes.py ir_plot.py \ 29 | nfc_gen_url.py nfc_hexdump.py nfc_prox2flip.py \ 30 | subghz_create_dat.py subghz_decode_presets.py subghz_gen_cmd.py \ 31 | subghz_histogram.py 32 | 33 | all: pylint 34 | 35 | gen-files: x10-brute ir-brute fan-brute nfc-Rick_Roll 36 | 37 | clone-repos: t119bruteforcer flipperzero-bruteforce CAMEbruteforcer Flipper-IRDB secplus 38 | 39 | lint: pylint 40 | 41 | pylint: 42 | for targ in ${FILES} ; do \ 43 | echo $$targ ; \ 44 | pylint -E $$targ ; \ 45 | done 46 | 47 | # pylint --load-plugins perflint $$targ ; \ 48 | # python -m py_compile $$targ ; \ 49 | # 50 | 51 | 52 | pep8: pycodestyle 53 | 54 | pycodestyle: 55 | for targ in ${FILES} ; do \ 56 | echo $$targ ; \ 57 | ${PYCODESTYLE} ${PEP8ARG} $$targ ; \ 58 | done 59 | 60 | 61 | # generage brute force files 62 | 63 | ir-brute: 64 | mkdir -p IR/All-Codes 65 | bash IR/.gen-all-ir.sh 66 | 67 | x10-brute: 68 | mkdir -p subghz/X10 69 | ( cd subghz/X10 ; ../../subghz_x10.py -b ) 70 | 71 | fan-brute: 72 | mkdir -p subghz/fan 73 | ( cd subghz/fan_bruteforce ; ../../subghz_create_dat.py fan ) 74 | 75 | nfc-Rick_Roll: 76 | mkdir -p nfc 77 | ./nfc_gen_url.py https://youtu.be/dQw4w9WgXcQ "Rick Roll" > nfc/Rick_Roll.nfc 78 | 79 | # used 3rd party libraries 80 | 81 | ndeflib: 82 | python3 -m pip install ndeflib 83 | 84 | secplus: 85 | git clone https://github.com/argilo/secplus.git 86 | 87 | 88 | #### 89 | 90 | clean: 91 | @/bin/rm -f *sub *.ir *.nfc touch_tunes-?? *_ON.sub *_OFF.sub X10_All-*.sub secv2-*.sub 92 | @/bin/rm -rf __pycache__ repos 93 | 94 | 95 | # clone usful repos with flipper datafiles 96 | 97 | t119bruteforcer: repos 98 | mkdir -p repos 99 | ( cd repos ; git clone https://github.com/xb8/t119bruteforcer.git ) 100 | 101 | flipperzero-bruteforce: 102 | mkdir -p repos 103 | ( cd repos ; git clone https://github.com/tobiabocchi/flipperzero-bruteforce.git ) 104 | 105 | CAMEbruteforcer: 106 | mkdir -p repos 107 | ( cd repos ; git clone https://github.com/BitcoinRaven/CAMEbruteforcer.git ) 108 | 109 | Flipper-IRDB: 110 | mkdir -p repos 111 | ( cd repos ; git clone https://github.com/logickworkshop/Flipper-IRDB.git ) 112 | 113 | -------------------------------------------------------------------------------- /ir_gen_all_codes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | ir_gen_all_codes.py (was gen_all_ir_codes.py) 4 | Generates file Flipper IR file will all command codes 5 | 6 | Written By: Peter Shipley github.com/evilpete 7 | 8 | From pkg https://github.com/evilpete/flipper_toolbox 9 | """ 10 | 11 | import sys 12 | import time 13 | 14 | MAX_BUTTONS = 256 15 | 16 | CMD_LEN = { 17 | 'RC5': 63, # 6 x40 18 | 'RC5X': 127, # 7 x80 19 | 'RC6': 256, # 8 x100 20 | 'NEC': 255, # 8 21 | # 'NECext': 255, # 16 x10000 22 | 'NECext': 65536, # 16 x10000 23 | 'NEC42': 255, # 8 24 | # 'NEC42ext': 65536, # 16 25 | 'Samsung32': 255, # 8 26 | 'SIRC': 255, # 8 27 | 'SIRC15': 255, # 8 28 | 'SIRC20': 255, # 8 29 | } 30 | 31 | hex_set = set('abcdefABCDEF0123456789') 32 | 33 | 34 | def is_hex_str(s): 35 | return set(s).issubset(hex_set) 36 | 37 | 38 | if __name__ == '__main__': 39 | 40 | if len(sys.argv) < 4: 41 | print(f""" 42 | Requires 3 args: 43 | {sys.argv[0]} PROTO ADDR SUBA 44 | 45 | {sys.argv[0]} NEC 40 00 46 | 47 | Valid proto {' '.join(CMD_LEN.keys())} 48 | """) 49 | sys.exit(1) 50 | 51 | PROTO = sys.argv[1] 52 | ADDR = sys.argv[2].upper() 53 | SUBA = sys.argv[3].upper() 54 | 55 | if PROTO not in CMD_LEN: 56 | print("Invalid IR Protocal") 57 | print(f"Valid proto {' '.join(CMD_LEN.keys())}") 58 | sys.exit(1) 59 | 60 | if ADDR.startswith('0X'): 61 | ADDR = ADDR[2:] 62 | 63 | if SUBA.startswith('0X'): 64 | SUBA = SUBA[2:] 65 | 66 | if not (is_hex_str(ADDR) and int(ADDR, 16) < 255 # noqa 67 | and is_hex_str(SUBA) and int(SUBA, 16) < 255): 68 | print("Invalid IR address or sub-address") 69 | print("Valid values hex 00 -> FF") 70 | sys.exit(1) 71 | 72 | if CMD_LEN[PROTO] > MAX_BUTTONS: 73 | print(f"limiting commands values to under {MAX_BUTTONS -1}") 74 | 75 | out_filen = f"IR-{PROTO}-{ADDR}-{SUBA}.ir" 76 | 77 | print(f"Creating file: {out_filen}") 78 | 79 | with open(out_filen, "w", encoding="utf-8") as fd: 80 | 81 | fd.write("Filetype: IR signals file\nVersion: 1\n") 82 | fd.write("# generated with flipper_toolbox\n") 83 | fd.write(f"# {time.ctime()}\n") 84 | 85 | # 256 button limit ( do you want 65536 buttons? ) 86 | cmd_limit_cnt = min(MAX_BUTTONS, CMD_LEN[PROTO]) 87 | 88 | for i in range(cmd_limit_cnt, -1, -1): 89 | fd.write(f"#\nname: Code_{i:03d}\ntype: parsed\n" 90 | f"protocol: {PROTO}\naddress: {ADDR} {SUBA} 00 00\n" 91 | f"command: {i & 0xFF:02X} {(i >> 8) & 0xFF:02X} 00 00\n") 92 | 93 | sys.exit(0) 94 | -------------------------------------------------------------------------------- /ir_plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | 4 | ir_plot.py 5 | 6 | plot data from flipper IR raw data save files 7 | 8 | Warning: this is 5 min hack code, use at own risk 9 | 10 | Written By: Peter Shipley github.com/evilpete 11 | 12 | From pkg https://github.com/evilpete/flipper_toolbox 13 | 14 | """ 15 | 16 | 17 | import sys 18 | import os 19 | # from statistics import mean 20 | import argparse 21 | from pprint import pprint 22 | import numpy as np 23 | # import pandas as pd 24 | import matplotlib.pyplot as plt 25 | 26 | PRINT_BITS = True # this is a hack 27 | 28 | 29 | def arg_opts(): 30 | 31 | parser = argparse.ArgumentParser(add_help=True, # noqa 32 | formatter_class=argparse.RawDescriptionHelpFormatter) 33 | 34 | parser.add_argument('-v', '--verbose', dest="verbose", 35 | default=0, 36 | help='Increase debug verbosity', action='count') 37 | 38 | parser.add_argument("-n", "--name", dest="cmd_name", 39 | default=None, 40 | help="IR Command Name") 41 | 42 | parser.add_argument("-f", "--file", dest="filename", 43 | default=None, 44 | help="IR Filename") 45 | 46 | parser.add_argument("-i", "--invert", dest="invert", 47 | default=False, 48 | action='store_true', 49 | help="Invert Wave plot") 50 | 51 | parser.add_argument("-d", "--dir", dest="destdir", 52 | default=None, 53 | help="Destination") 54 | 55 | parser.add_argument("-o", "--output", dest="out_format", 56 | choices=['png', 'pdf', 'svg'], 57 | default="None", 58 | help="Output Format") 59 | 60 | parser.add_argument("-s", "--screen", dest="screen", 61 | default=False, action='store_true', 62 | help="Display on Screen") 63 | 64 | # data_grp = parser.add_mutually_exclusive_group() 65 | 66 | return parser.parse_known_args() 67 | 68 | 69 | def load_cmd_data(filename): 70 | name = data_str = sig_type = duty_cycle = freq = None 71 | 72 | ret = [] 73 | with open(filename, 'r', encoding="utf-8") as fd: 74 | 75 | header = fd.readline().strip() 76 | if header != 'Filetype: IR signals file': 77 | print(f"Error: {filename} is not a 'Flipper IR signals file'") 78 | sys.exit(1) 79 | 80 | for line in fd: 81 | 82 | line = line.strip() 83 | 84 | if not line or line[0] == '#': # skip blank lines 85 | continue 86 | 87 | try: 88 | if line.startswith('name:'): 89 | name = line.split(':')[1].strip() 90 | sig_type = freq = duty_cycle = data_str = None 91 | 92 | if line.startswith('type:'): 93 | sig_type = line.split(':')[1].strip() 94 | 95 | if line.startswith('frequency:'): 96 | freq = line.split(':')[1].strip() 97 | 98 | if line.startswith('duty_cycle:'): 99 | duty_cycle = line.split(':')[1].strip() 100 | 101 | if line.startswith('data:'): 102 | data_str = line.split(':')[1].strip() 103 | 104 | except IndexError: 105 | print("Line format error: {line)") 106 | 107 | if name and sig_type and freq and duty_cycle and data_str: 108 | if sig_type == 'raw': 109 | dat = { 110 | 'name': name, 111 | 'type': sig_type, 112 | 'freq': freq, 113 | 'duty_cycle': duty_cycle, 114 | 'data_str': data_str, 115 | } 116 | ret.append(dat) 117 | 118 | name = sig_type = freq = duty_cycle = data_str = None 119 | 120 | return ret 121 | 122 | 123 | def split_data_str(dat, max_val=15000): 124 | 125 | dat_list = dat.split() 126 | ret = [] 127 | cur_dat = [] 128 | 129 | # print(f"dat_list: {len(dat_list)}") 130 | 131 | for x in dat_list: 132 | i = int(x) 133 | if i > max_val: 134 | ret.append(cur_dat) 135 | # print(f"cur_dat: {len(cur_dat)}") 136 | cur_dat = [] 137 | else: 138 | cur_dat.append(i) 139 | 140 | ret.append(cur_dat) 141 | 142 | return ret 143 | 144 | 145 | LOW_PLOT_VAL = 1 146 | HIGH_PLOT_VAL = 5 147 | 148 | 149 | def convert_dat(dat_list, invert=False, verbose=0): # normalize=0, 150 | 151 | high_val = HIGH_PLOT_VAL 152 | low_val = LOW_PLOT_VAL 153 | # print("== convert_dat") 154 | 155 | if len(dat_list) % 2 != 0: 156 | dat_list.append(0) 157 | 158 | dat_len = len(dat_list) 159 | 160 | if verbose > 1: 161 | print(f"dat_len {dat_len}") 162 | 163 | if invert: 164 | high_val = LOW_PLOT_VAL 165 | low_val = HIGH_PLOT_VAL 166 | 167 | # i_min = 15 168 | # # o_min = 23 169 | # if normalize: 170 | # e = dat_list[2::2] 171 | # i_min = min(e) // 10 172 | # if verbose > 2: 173 | # o = dat_list[3::2] 174 | # print(min(dat_list), mean(dat_list), 175 | # max(dat_list), "\n", dat_list, "\n") 176 | # print(min(e), mean(e), max(e), "\n", e, "\n") 177 | # print(min(o), mean(o), max(o), "\n", o, "\n") 178 | # print("\n\n") 179 | 180 | res = [low_val] 181 | for x in range(0, dat_len, 2): 182 | 183 | i = dat_list[x] # // 10 184 | # if normalize: 185 | # i = (i // i_min) * i_min 186 | 187 | j = int(dat_list[x + 1]) # // 10 188 | # if normalize: 189 | # j = (j // 23) * 26 190 | 191 | # print(f"{x}: {i} {j} {len(res)}") 192 | # res += [LOW_PLOT_VAL] * i 193 | # res += [HIGH_PLOT_VAL] * j 194 | 195 | res += [high_val] * i 196 | res += [low_val] * j 197 | res.append(1) 198 | 199 | # print("\n") 200 | return res 201 | 202 | 203 | # An Experiment 204 | def decode_dat(dat_list, verbose=0): 205 | 206 | dat_len = len(dat_list) 207 | if verbose > 1: 208 | print("== decode_dat") 209 | resA = [] 210 | resB = [] 211 | i = j = 0 212 | for x in range(0, dat_len, 2): 213 | 214 | i = dat_list[x] # // 10 215 | 216 | j = int(dat_list[x + 1]) # // 10 217 | 218 | A = '1' if i > j else '0' 219 | resA.append(A) 220 | B = '1' if j > 500 else '0' 221 | resB.append(B) 222 | 223 | # if verbose > 1:#test 224 | # print(f"{x:3d}: {i:4d} {j:4d} : {A} {B}") 225 | 226 | if verbose > 1: # test 227 | print(f"A: {resA}") 228 | print(f"B: {resB}") 229 | 230 | # def printbits 231 | # # this method is total hack and only works on a 232 | # if debug or PRINT_BITS: 233 | # # Print Bits 234 | # o = d[1::2] 235 | # avg_val = mean(o) 236 | # bits = ['0' if b < avg_val else '1' for b in o] 237 | # # print(o) 238 | # bit_str = "".join(bits) 239 | # print(bits, bit_str, "{0:02x} {0:d}".format(int(bit_str, 2))) 240 | 241 | 242 | def main(): 243 | 244 | filen = 'Test.ir' # None 245 | cmd_name = destdir = None 246 | verbose = 0 247 | 248 | arg, av = arg_opts() 249 | # print("arg", arg, "av=", av) 250 | 251 | disp = False 252 | 253 | if arg.verbose: 254 | verbose = arg.verbose 255 | 256 | # get input filename from argparse or fist arg 257 | if arg.filename: 258 | filen = arg.filename 259 | elif av: 260 | filen = av.pop(0) 261 | 262 | if filen is None: 263 | print('Usage:\n\tir_plot.py ') 264 | sys.exit(0) 265 | 266 | if not os.path.exists(filen): 267 | print(f"file {filen} not found") 268 | sys.exit(0) 269 | 270 | if arg.cmd_name: 271 | cmd_name = arg.cmd_name 272 | elif av: 273 | cmd_name = av.pop(0) 274 | 275 | if arg.destdir: 276 | destdir = arg.destdir 277 | if not os.path.exists(destdir): 278 | os.mkdir(destdir) 279 | 280 | # if arg.out_format in ['png', 'pdf', 'svg']: 281 | # if not os.path.exists(destdir): 282 | # os.mkdir(destdir) 283 | 284 | if arg.screen or arg.out_format not in ['png', 'pdf', 'svg']: 285 | disp = True 286 | 287 | # print("arg", arg, "disp=", disp) 288 | 289 | cmd_data = load_cmd_data(filen) 290 | 291 | # pprint(cmd_data, compact=True) 292 | # print(f"cmd_data len {len(cmd_data)}") 293 | 294 | if cmd_name: 295 | cmd_data = [x for x in cmd_data if cmd_name == x['name']] 296 | 297 | if not cmd_data: 298 | print(f'Was not able to find raw data for "{cmd_name}" in {filen}') 299 | sys.exit() 300 | 301 | if verbose: 302 | print(f"{len(cmd_data)} found in {filen}") 303 | 304 | if destdir is None and arg.out_format in ['png', 'pdf', 'svg']: 305 | if len(cmd_data) == 1: 306 | destdir = '.' 307 | else: # cmd_data is > 1 308 | destdir = os.path.splitext(filen)[0] 309 | 310 | # if verbose: 311 | # print(f"destdir = {destdir}") 312 | 313 | # if not os.path.exists(destdir): 314 | # os.mkdir(destdir) 315 | 316 | for dat in cmd_data: 317 | dat['data'] = split_data_str(dat['data_str']) 318 | 319 | ii = 0 320 | for dat in cmd_data: 321 | 322 | dat_lists = dat['data'] 323 | # name = dat['name'] 324 | 325 | plt.figure(ii) 326 | ax = plt.gca() 327 | ax.axes.yaxis.set_visible(False) 328 | plt.title(f"IR Signal: {dat['name']}") 329 | 330 | # if verbose > 1:#test 331 | # print(f'\n{ii} {name} == {len(dat_lists)}') 332 | 333 | if verbose > 2: 334 | pprint(dat_lists, indent=4, compact=True) 335 | 336 | list_lenghts = [] 337 | conv_dat_lists = [] 338 | y_off = 0 339 | for d in dat_lists: 340 | 341 | if verbose > 1: 342 | print('o:', d[1::2]) 343 | print('e:', d[0::2]) 344 | 345 | print(f"d: {d}") 346 | 347 | # normalize=True, 348 | n_dat = convert_dat(d, invert=arg.invert, verbose=arg.verbose) 349 | 350 | conv_dat_lists.append(n_dat) 351 | list_lenghts.append(len(n_dat)) 352 | 353 | if verbose > 1: 354 | print(f"d: {d}") 355 | 356 | # decode_dat(d) 357 | 358 | # print(list_lenghts) 359 | 360 | max_len = max(list_lenghts) 361 | # print(max_len) 362 | plot_x = np.arange(max_len) 363 | 364 | for d in conv_dat_lists: 365 | d_len = len(d) 366 | if d_len < max_len: 367 | ln = max_len - d_len 368 | d += [1] * ln 369 | 370 | plot_y = np.array(d) + (y_off * (HIGH_PLOT_VAL + 1)) 371 | 372 | plt.plot(plot_x, plot_y) 373 | 374 | # y_off += HIGH_PLOT_VAL + 1 375 | y_off += 1 376 | 377 | plt.gcf().set_size_inches(6, 1 + (.5 * y_off)) 378 | 379 | if arg.out_format == 'png': 380 | if arg.verbose: 381 | print(f'{destdir}/{ii}_{cmd_name}.png y_off={y_off}') 382 | plt.savefig(f'{destdir}/{ii}_{cmd_name}.png', pad_inches=0.3) 383 | 384 | ii += 1 385 | 386 | if disp: 387 | plt.show() 388 | 389 | 390 | if __name__ == '__main__': 391 | main() 392 | -------------------------------------------------------------------------------- /nfc/.rick-roll.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/nfc/.rick-roll.jpg -------------------------------------------------------------------------------- /nfc/README.md: -------------------------------------------------------------------------------- 1 | # Flipper Flipper NFC Files # 2 | 3 | A Collection of NFC Device Files 4 | 5 | --- 6 | 7 | 8 | #### [Rick_Roll.nfc](Rick_Roll.nfc) #### 9 | 10 | Flipper NFC device file to emulate a NTAG215 encoded with a Rick Roll URL when scanned 11 | 12 | Contains encoded URL [https://youtu.be/dQw4w9WgXcQ](https://youtu.be/dQw4w9WgXcQ) 13 | 14 | ( generated using [gen_url_nfc.py](../gen_url_nfc.py) ) 15 | 16 | --- 17 | -------------------------------------------------------------------------------- /nfc/Rick_Roll.nfc: -------------------------------------------------------------------------------- 1 | Filetype: Flipper NFC device 2 | Version: 2 3 | # generated with flipper_toolbox 4 | Device type: NTAG215 5 | # UID, ATQA and SAK are common for all formats 6 | UID: 04 10 56 01 74 48 03 7 | ATQA: 44 00 8 | SAK: 00 9 | # Mifare Ultralight specific data 10 | Signature: A5 80 A4 CC A0 C3 A1 F6 8B BE 6F EE 83 A6 B9 EE 36 F8 FB C8 14 5A 23 AA 29 DB 78 56 07 B9 6B 92 11 | Mifare version: 00 04 04 02 01 00 11 03 12 | Counter 0: 0 13 | Tearing 0: 00 14 | Counter 1: 0 15 | Tearing 1: 00 16 | Counter 2: 0 17 | Tearing 2: 00 18 | Pages total: 135 19 | Page 0: 04 10 56 CA 20 | Page 1: 01 74 48 03 21 | Page 2: 3E 48 00 00 22 | Page 3: E1 10 3E 00 23 | Page 4: 03 29 91 01 24 | Page 5: 15 55 04 79 25 | Page 6: 6F 75 74 75 26 | Page 7: 2E 62 65 2F 27 | Page 8: 64 51 77 34 28 | Page 9: 77 39 57 67 29 | Page 10: 58 63 51 51 30 | Page 11: 01 0C 54 02 31 | Page 12: 65 6E 52 69 32 | Page 13: 63 6B 20 52 33 | Page 14: 6F 6C 6C FE 34 | Page 15: 00 00 00 00 35 | Page 16: 00 00 00 00 36 | Page 17: 00 00 00 00 37 | Page 18: 00 00 00 00 38 | Page 19: 00 00 00 00 39 | Page 20: 00 00 00 00 40 | Page 21: 00 00 00 00 41 | Page 22: 00 00 00 00 42 | Page 23: 00 00 00 00 43 | Page 24: 00 00 00 00 44 | Page 25: 00 00 00 00 45 | Page 26: 00 00 00 00 46 | Page 27: 00 00 00 00 47 | Page 28: 00 00 00 00 48 | Page 29: 00 00 00 00 49 | Page 30: 00 00 00 00 50 | Page 31: 00 00 00 00 51 | Page 32: 00 00 00 00 52 | Page 33: 00 00 00 00 53 | Page 34: 00 00 00 00 54 | Page 35: 00 00 00 00 55 | Page 36: 00 00 00 00 56 | Page 37: 00 00 00 00 57 | Page 38: 00 00 00 00 58 | Page 39: 00 00 00 00 59 | Page 40: 00 00 00 00 60 | Page 41: 00 00 00 00 61 | Page 42: 00 00 00 00 62 | Page 43: 00 00 00 00 63 | Page 44: 00 00 00 00 64 | Page 45: 00 00 00 00 65 | Page 46: 00 00 00 00 66 | Page 47: 00 00 00 00 67 | Page 48: 00 00 00 00 68 | Page 49: 00 00 00 00 69 | Page 50: 00 00 00 00 70 | Page 51: 00 00 00 00 71 | Page 52: 00 00 00 00 72 | Page 53: 00 00 00 00 73 | Page 54: 00 00 00 00 74 | Page 55: 00 00 00 00 75 | Page 56: 00 00 00 00 76 | Page 57: 00 00 00 00 77 | Page 58: 00 00 00 00 78 | Page 59: 00 00 00 00 79 | Page 60: 00 00 00 00 80 | Page 61: 00 00 00 00 81 | Page 62: 00 00 00 00 82 | Page 63: 00 00 00 00 83 | Page 64: 00 00 00 00 84 | Page 65: 00 00 00 00 85 | Page 66: 00 00 00 00 86 | Page 67: 00 00 00 00 87 | Page 68: 00 00 00 00 88 | Page 69: 00 00 00 00 89 | Page 70: 00 00 00 00 90 | Page 71: 00 00 00 00 91 | Page 72: 00 00 00 00 92 | Page 73: 00 00 00 00 93 | Page 74: 00 00 00 00 94 | Page 75: 00 00 00 00 95 | Page 76: 00 00 00 00 96 | Page 77: 00 00 00 00 97 | Page 78: 00 00 00 00 98 | Page 79: 00 00 00 00 99 | Page 80: 00 00 00 00 100 | Page 81: 00 00 00 00 101 | Page 82: 00 00 00 00 102 | Page 83: 00 00 00 00 103 | Page 84: 00 00 00 00 104 | Page 85: 00 00 00 00 105 | Page 86: 00 00 00 00 106 | Page 87: 00 00 00 00 107 | Page 88: 00 00 00 00 108 | Page 89: 00 00 00 00 109 | Page 90: 00 00 00 00 110 | Page 91: 00 00 00 00 111 | Page 92: 00 00 00 00 112 | Page 93: 00 00 00 00 113 | Page 94: 00 00 00 00 114 | Page 95: 00 00 00 00 115 | Page 96: 00 00 00 00 116 | Page 97: 00 00 00 00 117 | Page 98: 00 00 00 00 118 | Page 99: 00 00 00 00 119 | Page 100: 00 00 00 00 120 | Page 101: 00 00 00 00 121 | Page 102: 00 00 00 00 122 | Page 103: 00 00 00 00 123 | Page 104: 00 00 00 00 124 | Page 105: 00 00 00 00 125 | Page 106: 00 00 00 00 126 | Page 107: 00 00 00 00 127 | Page 108: 00 00 00 00 128 | Page 109: 00 00 00 00 129 | Page 110: 00 00 00 00 130 | Page 111: 00 00 00 00 131 | Page 112: 00 00 00 00 132 | Page 113: 00 00 00 00 133 | Page 114: 00 00 00 00 134 | Page 115: 00 00 00 00 135 | Page 116: 00 00 00 00 136 | Page 117: 00 00 00 00 137 | Page 118: 00 00 00 00 138 | Page 119: 00 00 00 00 139 | Page 120: 00 00 00 00 140 | Page 121: 00 00 00 00 141 | Page 122: 00 00 00 00 142 | Page 123: 00 00 00 00 143 | Page 124: 00 00 00 00 144 | Page 125: 00 00 00 00 145 | Page 126: 00 00 00 00 146 | Page 127: 00 00 00 00 147 | Page 128: 00 00 00 00 148 | Page 129: 00 00 00 00 149 | Page 130: 00 00 00 BD 150 | Page 131: 04 00 00 FF 151 | Page 132: 00 05 00 00 152 | Page 133: 00 00 00 00 153 | Page 134: 00 00 00 00 154 | -------------------------------------------------------------------------------- /nfc_dict_diff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | nfc_dict_diff.py : strip diff dict key files 4 | Written By: Peter Shipley github.com/evilpete 5 | 6 | From pkg https://github.com/evilpete/flipper_toolbox 7 | """ 8 | 9 | # Quick script to diff two Flipper NFC dict lists 10 | 11 | # 12 | # nfc_dict_diff.py mf_classic_dict_user.nfc new-mf_classic_dict_user.nfc 13 | # 14 | 15 | import sys 16 | 17 | file1 = sys.argv[1] 18 | file2 = sys.argv[2] 19 | 20 | COL = 6 21 | 22 | print(file1, file2) 23 | 24 | with open(file1, 'r', encoding="utf-8") as fd: 25 | list_A = [line.strip() for line in fd if line[0] != '#' and len(line) > 4] 26 | print("list_A", len(list_A), file1) 27 | set_A = set(list_A) 28 | 29 | with open(file2, 'r', encoding="utf-8") as fd: 30 | list_B = [line.strip() for line in fd if line[0] != '#' and len(line) > 4] 31 | print("list_B", len(list_B), file2) 32 | set_B = set(list_B) 33 | 34 | diff_AB = set_A.difference(set_B) 35 | # print("diff_AB", diff_AB) 36 | print("-------") 37 | print("diff list_A - list_B =", len(diff_AB)) 38 | 39 | 40 | print(f"Unique to {file1}") 41 | len_AB = len(diff_AB) 42 | list_AB = sorted(list(diff_AB)) 43 | for X in range(1, len_AB, COL): 44 | print(" ".join(list_AB[X:X + COL])) 45 | 46 | diff_BA = set_B.difference(set_A) 47 | # print("diff_BA", diff_BA) 48 | print("-------") 49 | print("diff list_B - list_A =", len(diff_BA)) 50 | 51 | 52 | print(f"Unique to {file2}") 53 | len_BA = len(diff_BA) 54 | list_BA = sorted(list(diff_BA)) 55 | for X in range(1, len_BA, COL): 56 | print(" ".join(list_BA[X:X + COL])) 57 | 58 | common_AB = set_A.intersection(set_B) 59 | print("-------") 60 | print("Intersection list_A and list_B =", len(common_AB)) 61 | list_common = sorted(list(common_AB)) 62 | for X in range(1, len_BA, COL): 63 | print(" ".join(list_common[X:X + COL])) 64 | -------------------------------------------------------------------------------- /nfc_dict_strip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | nfc_dict_strip.py : strip duplicate dict keys 5 | Written By: Peter Shipley github.com/evilpete 6 | 7 | From pkg https://github.com/evilpete/flipper_toolbox 8 | """ 9 | 10 | # Quick script to comment out or strip duplicate dict keys 11 | 12 | # 13 | # nfc_dict_strip.py mf_classic_dict.nfc mf_classic_dict_user.nfc > mf_classic_dict_user_unique.nfc 14 | # 15 | 16 | import sys 17 | import time 18 | 19 | file1 = file2 = del_dups = None 20 | _debug = 0 21 | 22 | gen_str = "# Generated with https://github.com/evilpete/flipper_toolbox" 23 | 24 | 25 | def dict_strip(file_1, file_2): 26 | 27 | with open(file_1, 'r', encoding="utf-8") as fd: 28 | list_A = [line.strip().upper() for line in fd if line[0] != '#' and len(line) > 4] 29 | # print("list_A", len(list_A), file_1) 30 | set_A = set(list_A) 31 | 32 | with open(file_2, 'r', encoding="utf-8") as fd: 33 | for line in fd: 34 | if _debug: 35 | print(f">>> {line}", end="", file=sys.stderr) 36 | if line[0] == '#': 37 | if not line.startswith("# Generated "): 38 | print(line, end="") 39 | continue 40 | dat = line[:12].upper() 41 | if _debug: 42 | print("line len =", len(line), file=sys.stderr) 43 | print("DAT len =", len(dat), file=sys.stderr) 44 | if dat in set_A: 45 | if del_dups is None: 46 | print(f"#- {line}", end="") 47 | else: 48 | print(dat) 49 | # print(line, end="") 50 | 51 | print(gen_str) 52 | print(f"# Generated {time.ctime()}") 53 | 54 | 55 | if __name__ == '__main__': 56 | 57 | av = sys.argv[1:] 58 | 59 | if av and av[0] == '-h': 60 | print("Usage:\n\tnfc_dict_strip.py [-d ] dict_file_A dict_file_B > dict_nodups\n") 61 | print("\tremoves duplicate keys, outputing only keys unique to dict_file_B") 62 | sys.exit(0) 63 | 64 | if av and av[0] == '-d': 65 | av.pop(0) 66 | del_dups = True 67 | 68 | if av: 69 | file1 = av.pop(0) 70 | if av: 71 | file2 = av.pop(0) 72 | 73 | if not (file1 and file2): 74 | print("Two filesnames required", file=sys.stderr) 75 | sys.exit(0) 76 | 77 | dict_strip(file1, file2) 78 | -------------------------------------------------------------------------------- /nfc_flip2prox.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | nfc_flip2prox.py 4 | Written By: Peter Shipley github.com/evilpete 5 | 6 | From pkg https://github.com/evilpete/flipper_toolbox 7 | """ 8 | 9 | # pylint: disable=line-too-long 10 | 11 | import os 12 | import sys 13 | import binascii 14 | # import pprint 15 | import json 16 | 17 | 18 | FLIPPER_NFC_FILETYPE = 'Flipper NFC device' 19 | blk_data = [] 20 | 21 | _debug = 0 22 | 23 | 24 | def read_data(inFile): 25 | 26 | with open(inFile, 'r', encoding="utf-8") as fd: 27 | 28 | line = fd.readline().strip() 29 | 30 | if line.startswith("Filetype:"): 31 | a = line.split(':', 1) 32 | if not a[1].strip().startswith(FLIPPER_NFC_FILETYPE): 33 | print(f"Error: {inFile} is not a Flipper NFC data file") 34 | if _debug: 35 | print(f">>{line}<<") 36 | sys.exit(1) 37 | else: 38 | print(f"Error: {inFile} is not a Flipper NFC data file") 39 | if _debug: 40 | print(f">>>{line}<<<") 41 | sys.exit(1) 42 | 43 | for line in fd: 44 | line = line.strip() 45 | 46 | if not line or line[0] == '#': # skip blank lines 47 | continue 48 | 49 | if line.startswith("Device type:"): 50 | a = line.split(':', 1) 51 | if not a[1].strip().startswith('Mifare'): 52 | print(f"Error: {inFile} is not a Mifare data file: {a[1].strip()}") 53 | if _debug: 54 | print(f">>>>{line}<<<<") 55 | print(f">>>>{a[0]}<<<<") 56 | print(f">>>>{a[1]}<<<<") 57 | sys.exit(1) 58 | 59 | if line.startswith("Block"): 60 | b = line.split(':', 1)[1].strip() 61 | blk_data.append(b.split()) 62 | 63 | 64 | def write_mct_data(outFile): 65 | i = 0 66 | sec_cnt = 0 67 | with open(outFile, 'w', encoding="utf-8") as fd: 68 | for b in blk_data: 69 | if (i % 4) == 0: 70 | print(f"+Sector: {sec_cnt}", file=fd) 71 | sec_cnt += 1 72 | i += 1 73 | # hex_str = "".join(b).upper() 74 | # print(hex_str, file=fd) 75 | print("".join(b).upper(), file=fd) 76 | 77 | 78 | def write_bin_data(outFile): 79 | with open(outFile + '.bin', 'wb') as fd: 80 | for b in blk_data: 81 | # hex_str = "".join(b) 82 | # bin_str = binascii.unhexlify(hex_str) 83 | # fd.write(bin_str) 84 | fd.write(binascii.unhexlify("".join(b))) 85 | 86 | 87 | # {"id":"5b4bebb8-f32e-4021-a65b-f73f9fc29e6e","uid":"1C CA 76 A0","sak":8,"atqa":[0,4],"name":"chaminade","tag":3,"color":"#ff5722", 88 | # [ str(int(x, 16)) for x in aa] 89 | # [1C CA 76 A0] 00 [08] 04 00 03 4D 90 1A FF 4A 0A 1D 90 | def write_cham_data(outFile): 91 | 92 | dat_s = blk_data[0] 93 | dat_i = [int(x, 16) for x in dat_s] 94 | j_data = [[]] * 256 95 | dat = { 96 | "id": ' '.join(dat_s[:4]), 97 | "uid": ' '.join(dat_s[:4]), 98 | "sak": dat_i[5], 99 | "atqa": [dat_i[7], dat_i[6]], 100 | "name": out_filen, 101 | "tag": 3, 102 | "color": "#ff5722", 103 | "data": j_data, 104 | } 105 | 106 | i = 0 107 | for b in blk_data: 108 | j_data[i] = [int(x, 16) for x in b] 109 | i += 1 110 | 111 | # with open(outFile + ".pp", 'w', encoding="utf-8") as fd: 112 | # pprint.pprint(dat, stream=fd) 113 | 114 | with open(outFile + "_cm.json", 'w', encoding="utf-8") as fd: 115 | json.dump(dat, fd) 116 | 117 | 118 | def write_eml_data(outFile): 119 | with open(outFile + ".eml", 'w', encoding="utf-8") as fd: 120 | for b in blk_data: 121 | hex_str = "".join(b).upper() 122 | print(hex_str, file=fd) 123 | 124 | 125 | def write_mcj_data(outFile): 126 | b_data = {} 127 | dat = { 128 | "Created": "MifareClassicTool", 129 | "FileType": "mfcard", 130 | "blocks": b_data 131 | } 132 | 133 | i = 0 134 | for b in blk_data: 135 | b_data[str(i)] = "".join(b).upper() 136 | i += 1 137 | 138 | with open(outFile + "_mct.json", 'w', encoding="utf-8") as fd: 139 | json.dump(dat, fd, indent=2) 140 | 141 | 142 | write_funcs = { 143 | 'eml': (write_eml_data, "proxmark emulator"), # proxmark emulator 144 | 'bin': (write_bin_data, "proxmark/Chameleon bin format"), # proxmark 145 | 'mct': (write_mct_data, "MIFARE Classic Tool"), # MIFARE Classic Tool 146 | 'mfj': (write_mcj_data, "MIFARE Classic Tool Json"), # MIFARE Classic Tool Json 147 | 'json': (write_cham_data, "proxmark/Chameleonn Json format"), # Chameleon Json format 148 | } 149 | 150 | if __name__ == '__main__': 151 | 152 | out_format = None 153 | out_filen = None 154 | 155 | av = sys.argv[1:] 156 | 157 | if av and av[0] == '-h': 158 | print("\tnfc_conv.py [-f output_format] input_filename [output_filename]") 159 | 160 | print("\n\tValid formats:") 161 | for k, v in write_funcs.items(): 162 | print(f"\t\t{k}:\t{v[1]}") 163 | sys.exit(1) 164 | 165 | 166 | if av and av[0] == '-f': 167 | av.pop(0) 168 | if av: 169 | out_format = av.pop(0) 170 | else: 171 | print("Error: '-f' option requires format name") 172 | sys.exit(1) 173 | if out_format not in write_funcs: 174 | print("Error: unknown format") 175 | print(" Known Formats:", " ".join(write_funcs.keys())) 176 | sys.exit(1) 177 | 178 | if av: 179 | in_filen = av.pop(0) 180 | else: 181 | print("Error: missing input filename") 182 | sys.exit(1) 183 | 184 | if av: 185 | fn = av.pop(0) 186 | out_info = os.path.splitext(fn) 187 | out_format = out_info[1][1:] 188 | out_filen = out_info[0] 189 | else: 190 | out_filen = os.path.splitext(in_filen)[0] 191 | 192 | if _debug: 193 | print(f"in file: {in_filen}") 194 | print(f"out file: {out_filen}") 195 | print(f"out format: {out_format}") 196 | 197 | read_data(in_filen) 198 | 199 | if out_format in write_funcs: 200 | write_funcs[out_format][0](out_filen) 201 | else: 202 | print(f"unknown output format {out_format}") 203 | 204 | # write_eml_data(out_filen) 205 | -------------------------------------------------------------------------------- /nfc_gen_phone.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Generates NFC with a telephone number and saves to a Flipper NFC "save" file 4 | No Guarantees. 5 | 6 | ./nfc_gen_phone.py +375296049766 +1 7 | requires ndeflib 8 | 9 | Original Code by: Peter Shipley github.com/evilpete 10 | Modified by: Sage https://github.com/misusage 11 | 12 | From pkg https://github.com/evilpete/flipper_toolbox 13 | """ 14 | 15 | import sys 16 | try: 17 | import ndef 18 | except ImportError as e: 19 | print("Failed to import ndef") 20 | print("https://github.com/nfcpy/ndeflib") 21 | sys.exit(0) 22 | 23 | # for debuging 24 | verbose = 0 25 | 26 | nfc_header = """Filetype: Flipper NFC device 27 | Version: 2 28 | # generated with flipper_toolbox 29 | Device type: NTAG215 30 | # UID, ATQA and SAK are common for all formats 31 | UID: 04 10 56 01 74 48 03 32 | ATQA: 44 00 33 | SAK: 00 34 | # Mifare Ultralight specific data 35 | Signature: A5 80 A4 CC A0 C3 A1 F6 8B BE 6F EE 83 A6 B9 EE 36 F8 FB C8 14 5A 23 AA 29 DB 78 56 07 B9 6B 92 36 | Mifare version: 00 04 04 02 01 00 11 03 37 | Counter 0: 0 38 | Tearing 0: 00 39 | Counter 1: 0 40 | Tearing 1: 00 41 | Counter 2: 0 42 | Tearing 2: 00 43 | Pages total: 135 44 | """ 45 | 46 | data_static = [ 47 | 0x04, 0x10, 0x56, 0xCA, # 01 serial number 48 | 0x01, 0x74, 0x48, 0x03, # 02 serial number 49 | 0x3E, 0x48, 0x00, 0x00, # 03 serial number, internal, lock bytes, lock bytes 50 | 0xE1, 0x10, 0x3E, 0x00, # 04 Capability Container 51 | ] 52 | 53 | conf_pages = [ 54 | 0x00, 0x00, 0x00, 0xBD, # 130 55 | 0x04, 0x00, 0x00, 0xFF, # 131 56 | 0x00, 0x05, 0x00, 0x00, # 132 57 | 0x00, 0x00, 0x00, 0x00, # 133 58 | 0x00, 0x00, 0x00, 0x00, # 134 59 | ] 60 | 61 | 62 | def print_nfc_sub(t_data, file=sys.stdout): 63 | 64 | print(nfc_header, end='', file=file) 65 | 66 | p = 0 67 | for x in range(0, 540, 4): 68 | print(f"Page {p}: {t_data[x]:02X} {t_data[x + 1]:02X} " 69 | f"{t_data[x + 2]:02X} {t_data[x + 3]:02X}", file=file) 70 | p = p + 1 71 | 72 | 73 | def write_nfc_sub(t_data): 74 | 75 | print(nfc_header, end='', file=f) 76 | 77 | p = 0 78 | for x in range(0, 540, 4): 79 | print(f"Page {p}: {t_data[x]:02X} {t_data[x + 1]:02X} " 80 | f"{t_data[x + 2]:02X} {t_data[x + 3]:02X}", file=f) 81 | p = p + 1 82 | 83 | 84 | def gen_nfc_sub(tag_data): 85 | 86 | tag_message = [] 87 | data_list = [] 88 | 89 | for x in tag_data: 90 | if x[0] == 'url': 91 | tag_message.append(ndef.UriRecord(x[1])) 92 | 93 | if verbose: 94 | print(x, file=sys.stderr) 95 | print(tag_message[-1:], file=sys.stderr) 96 | 97 | buf = b"".join((ndef.message_encoder(tag_message))) 98 | 99 | m_len = len(buf) 100 | 101 | if verbose: 102 | print("buf", m_len, hex(m_len), buf, file=sys.stderr) 103 | 104 | data_list.extend(data_static) 105 | data_list.append(3) # Message Flags 106 | data_list.append(m_len) # Type Length 107 | data_list.extend(list(buf)) 108 | 109 | data_list.append(0xFE) # end of Data 110 | 111 | data_len = len(data_list) 112 | 113 | if verbose: 114 | print("data_list", data_len, data_list, file=sys.stderr) 115 | 116 | x = 520 - data_len 117 | data_list.extend([0] * x) 118 | data_list.extend(conf_pages) 119 | 120 | return data_list 121 | 122 | 123 | if __name__ == '__main__': 124 | 125 | filename = "call-me.nfc" 126 | telstr = "tel:" 127 | arg_data = [] 128 | 129 | for a in sys.argv[1:]: 130 | if a.isdigit(): 131 | 132 | for b in sys.argv[2:]: 133 | if b.startswith("+"): 134 | a = b + a 135 | else: 136 | exit 137 | 138 | a = str(telstr) + a #add the tel: nfc code 139 | arg_data.append(("url", a)) 140 | 141 | #for i in arg_data: 142 | # print(i, end = ' ') 143 | #print("\n") 144 | else: 145 | exit 146 | 147 | if not arg_data: 148 | print("requires two URL arguments\n" 149 | "Please provide phone number with area code THEN the +country-code.\n" 150 | f"{sys.argv[0]} 12345679876 +1") 151 | sys.exit() 152 | 153 | nfc_data = gen_nfc_sub(arg_data) 154 | 155 | print_nfc_sub(nfc_data) 156 | 157 | if verbose: 158 | for i in arg_data: 159 | print(i[0],i[1]) 160 | print_nfc_sub(nfc_data) 161 | else: 162 | with open(filename, mode='w') as f: 163 | write_nfc_sub(nfc_data) 164 | print("\nDone! NFC File has been created: " + filename) 165 | 166 | sys.exit() 167 | -------------------------------------------------------------------------------- /nfc_gen_url.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Generates NFC with URL address data and outputs Flipper NFC "save" file 4 | this is a 5 min hack, No Guarantees 5 | 6 | ./nfc_gen_url.py https://youtu.be/dQw4w9WgXcQ "Rick Roll" > rickroll.nfc 7 | 8 | requires ndeflib 9 | 10 | Written By: Peter Shipley github.com/evilpete 11 | 12 | From pkg https://github.com/evilpete/flipper_toolbox 13 | """ 14 | 15 | import sys 16 | import time 17 | 18 | try: 19 | import ndef 20 | except ImportError as e: 21 | print("Failed to import ndef") 22 | print("https://github.com/nfcpy/ndeflib") 23 | sys.exit(0) 24 | 25 | # make sure output is utf-8 26 | sys.stdout.reconfigure(encoding='utf-8') 27 | 28 | # for debuging 29 | verbose = 0 30 | 31 | nfc_header = (f"""Filetype: Flipper NFC device 32 | Version: 2 33 | # generated with flipper_toolbox 34 | # {time.ctime()} 35 | Device type: NTAG215 36 | # UID, ATQA and SAK are common for all formats 37 | UID: 04 10 56 01 74 48 03 38 | ATQA: 44 00 39 | SAK: 00 40 | # Mifare Ultralight specific data 41 | Signature: A5 80 A4 CC A0 C3 A1 F6 8B BE 6F EE 83 A6 B9 EE 36 F8 FB C8 14 5A 23 AA 29 DB 78 56 07 B9 6B 92 42 | Mifare version: 00 04 04 02 01 00 11 03 43 | Counter 0: 0 44 | Tearing 0: 00 45 | Counter 1: 0 46 | Tearing 1: 00 47 | Counter 2: 0 48 | Tearing 2: 00 49 | Pages total: 135 50 | """) 51 | 52 | data_static = [ 53 | 0x04, 0x10, 0x56, 0xCA, # 01 serial number 54 | 0x01, 0x74, 0x48, 0x03, # 02 serial number 55 | 0x3E, 0x48, 0x00, 0x00, # 03 serial number, internal, lock bytes, lock bytes 56 | 0xE1, 0x10, 0x3E, 0x00, # 04 Capability Container 57 | ] 58 | 59 | conf_pages = [ 60 | 0x00, 0x00, 0x00, 0xBD, # 130 61 | 0x04, 0x00, 0x00, 0xFF, # 131 62 | 0x00, 0x05, 0x00, 0x00, # 132 63 | 0x00, 0x00, 0x00, 0x00, # 133 64 | 0x00, 0x00, 0x00, 0x00, # 134 65 | ] 66 | 67 | 68 | def print_nfc_sub(t_data, file=sys.stdout): 69 | 70 | print(nfc_header, end='', file=file) 71 | 72 | p = 0 73 | for x in range(0, 540, 4): 74 | print(f"Page {p}: {t_data[x]:02X} {t_data[x + 1]:02X} " 75 | f"{t_data[x + 2]:02X} {t_data[x + 3]:02X}", file=file) 76 | p = p + 1 77 | 78 | 79 | def gen_nfc_sub(tag_data): 80 | 81 | tag_message = [] 82 | data_list = [] 83 | 84 | for x in tag_data: 85 | if x[0] == 'txt': 86 | tag_message.append(ndef.TextRecord(x[1], "en")) 87 | elif x[0] == 'url': 88 | tag_message.append(ndef.UriRecord(x[1])) 89 | 90 | if verbose: 91 | print(x, file=sys.stderr) 92 | print(tag_message[-1:], file=sys.stderr) 93 | 94 | buf = b"".join((ndef.message_encoder(tag_message))) 95 | 96 | m_len = len(buf) 97 | 98 | if verbose: 99 | print("buf", m_len, hex(m_len), buf, file=sys.stderr) 100 | 101 | data_list.extend(data_static) 102 | data_list.append(3) # Message Flags 103 | if m_len < 255: 104 | data_list.append(m_len) # Type Length 105 | else: 106 | data_list.append(0xFF) 107 | lenStr = hex(m_len)[2:].rjust(4, "0") 108 | data_list.append(int(lenStr[0:2], 16)) 109 | data_list.append(int(lenStr[2:4], 16)) 110 | data_list.extend(list(buf)) 111 | 112 | data_list.append(0xFE) # end of Data 113 | 114 | data_len = len(data_list) 115 | 116 | if verbose: 117 | print("data_list", data_len, data_list, file=sys.stderr) 118 | 119 | x = 520 - data_len 120 | data_list.extend([0] * x) 121 | data_list.extend(conf_pages) 122 | 123 | return data_list 124 | 125 | 126 | # url_str = "https://youtu.be/dQw4w9WgXcQ" 127 | # title_str = "Rick Roll" 128 | 129 | if __name__ == '__main__': 130 | 131 | arg_data = [] 132 | 133 | for a in sys.argv[1:]: 134 | if a.startswith("http"): 135 | arg_data.append(("url", a)) 136 | else: 137 | arg_data.append(("txt", a)) 138 | 139 | if not arg_data: 140 | print("requires at least one URL argument\n" 141 | "Please provide URL and optional Title\n\n" 142 | f"{sys.argv[0]} : https://some.url 'Title'") 143 | sys.exit() 144 | 145 | nfc_data = gen_nfc_sub(arg_data) 146 | 147 | print_nfc_sub(nfc_data) 148 | 149 | sys.exit() 150 | -------------------------------------------------------------------------------- /nfc_gen_wifi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Generates NFC with WiFi network data and saves to a Flipper NFC "save" file 4 | No Guarantees. 5 | 6 | ./nfc_gen_wifi.py 7 | requires ndeflib 8 | 9 | Original Code by: Peter Shipley github.com/evilpete 10 | Modified by: Sage https://github.com/misusage 11 | 12 | From pkg https://github.com/evilpete/flipper_toolbox 13 | """ 14 | 15 | import sys 16 | try: 17 | import ndef 18 | except ImportError as e: 19 | print("Failed to import ndef") 20 | print("https://github.com/nfcpy/ndeflib") 21 | sys.exit(0) 22 | 23 | # for debuging 24 | verbose = 0 25 | 26 | nfc_header = """Filetype: Flipper NFC device 27 | Version: 2 28 | # generated with flipper_toolbox 29 | Device type: NTAG215 30 | # UID, ATQA and SAK are common for all formats 31 | UID: 04 10 56 01 74 48 03 32 | ATQA: 44 00 33 | SAK: 00 34 | # Mifare Ultralight specific data 35 | Signature: A5 80 A4 CC A0 C3 A1 F6 8B BE 6F EE 83 A6 B9 EE 36 F8 FB C8 14 5A 23 AA 29 DB 78 56 07 B9 6B 92 36 | Mifare version: 00 04 04 02 01 00 11 03 37 | Counter 0: 0 38 | Tearing 0: 00 39 | Counter 1: 0 40 | Tearing 1: 00 41 | Counter 2: 0 42 | Tearing 2: 00 43 | Pages total: 135 44 | """ 45 | 46 | data_static = [ 47 | 0x04, 0x10, 0x56, 0xCA, # 01 serial number 48 | 0x01, 0x74, 0x48, 0x03, # 02 serial number 49 | 0x3E, 0x48, 0x00, 0x00, # 03 serial number, internal, lock bytes, lock bytes 50 | 0xE1, 0x10, 0x3E, 0x00, # 04 Capability Container 51 | ] 52 | 53 | conf_pages = [ 54 | 0x00, 0x00, 0x00, 0xBD, # 130 55 | 0x04, 0x00, 0x00, 0xFF, # 131 56 | 0x00, 0x05, 0x00, 0x00, # 132 57 | 0x00, 0x00, 0x00, 0x00, # 133 58 | 0x00, 0x00, 0x00, 0x00, # 134 59 | ] 60 | 61 | def print_nfc_sub(t_data, file=sys.stdout): 62 | 63 | print(nfc_header, end='', file=file) 64 | 65 | p = 0 66 | for x in range(0, 540, 4): 67 | print(f"Page {p}: {t_data[x]:02X} {t_data[x + 1]:02X} " 68 | f"{t_data[x + 2]:02X} {t_data[x + 3]:02X}", file=file) 69 | p = p + 1 70 | 71 | 72 | def write_nfc_sub(t_data): 73 | 74 | print(nfc_header, end='', file=f) 75 | 76 | p = 0 77 | for x in range(0, 540, 4): 78 | print(f"Page {p}: {t_data[x]:02X} {t_data[x + 1]:02X} " 79 | f"{t_data[x + 2]:02X} {t_data[x + 3]:02X}", file=f) 80 | p = p + 1 81 | 82 | 83 | def gen_nfc_sub(tag_data): 84 | 85 | mac = "FF:FF:FF:FF:FF:FF" 86 | data_list = [] 87 | credential = ndef.wifi.Credential() 88 | credential.set_attribute('network-index', 1) 89 | 90 | for x in tag_data: 91 | if x[0] == 'ssid': 92 | ssidStr = bytes(x[1], encoding="utf-8") 93 | credential.set_attribute(x[0], ssidStr) 94 | elif x[0] == 'authentication-type': 95 | credential.set_attribute(x[0], x[1]) 96 | elif x[0] == 'encryption-type': 97 | credential.set_attribute(x[0], x[1]) 98 | elif x[0] == 'network-key': 99 | keyStr = bytes(x[1], encoding="utf-8") 100 | credential.set_attribute(x[0], keyStr) 101 | elif x[0] == 'mac-address': 102 | mac = bytes.fromhex(mac.replace(':', '')) 103 | credential.set_attribute(x[0], mac) 104 | 105 | record = ndef.wifi.WifiSimpleConfigRecord() 106 | record.name = 'my config token' 107 | record.set_attribute('credential', credential) 108 | 109 | for x in tag_data: 110 | if x[0] == 'rf-bands': 111 | record.set_attribute(x[0], x[1]) 112 | 113 | buf = b"".join((ndef.message_encoder([record]))) 114 | m_len = len(buf) 115 | 116 | data_list.extend(data_static) 117 | data_list.append(3) # Message Flags 118 | data_list.append(m_len) # Type Length 119 | data_list.extend(list(buf)) 120 | data_list.append(0xFE) # end of Data 121 | 122 | data_len = len(data_list) 123 | 124 | if verbose: 125 | print("Verbose Mode ON") 126 | for b in tag_data: 127 | print(b, file=sys.stderr) 128 | print("Broadcast Mac: " + str(mac)) 129 | print("WiFi Credential Token: " + str(credential)) 130 | print(record) 131 | print("Buffer:", m_len, hex(m_len), buf, file=sys.stderr) 132 | print("Data List:", data_len, data_list, file=sys.stderr) 133 | 134 | x = 520 - data_len 135 | data_list.extend([0] * x) 136 | data_list.extend(conf_pages) 137 | 138 | return data_list 139 | 140 | 141 | if __name__ == '__main__': 142 | 143 | filename = "WiFi.nfc" 144 | arg_data = [] 145 | 146 | print("NFC WiFi Tag Generator for the Flipper Zero\n" 147 | "###########################################\n") 148 | ssid = input("What is the SSID?: ") 149 | arg_data.append(("ssid", ssid)) 150 | 151 | print("Choose an Authentication Type:") 152 | authChoice = input("Enter 1 for WPA/WPA2-Personal\nEnter 2 for WPA/WPA2-Enterprise (might not work)\n> ") 153 | if authChoice == "1": 154 | auth = "WPA2-Personal" 155 | arg_data.append(("authentication-type", auth)) 156 | elif authChoice == "2": 157 | auth = "WPA2-Enterprise" 158 | arg_data.append(("authentication-type", auth)) 159 | else: 160 | print("ERROR: You typed something else. Exiting...") 161 | sys.exit() 162 | 163 | print("\nChoose an Encryption Type:") 164 | encChoice = input("Enter 1 for AES (Select this option if you don't know)\nEnter 2 for TKIP\n> ") 165 | if encChoice == "1": 166 | encryption = "AES" 167 | arg_data.append(("encryption-type", encryption)) 168 | elif encChoice == "2": 169 | encryption = "TKIP" 170 | arg_data.append(("encryption-type", encryption)) 171 | else: 172 | print("ERROR: You typed something else. Exiting...") 173 | sys.exit() 174 | 175 | password = input("\nWhat is the WiFi Password?: ") 176 | arg_data.append(("network-key", password)) 177 | 178 | print("\nWhat WiFi Band is the network operating on?:") 179 | bandChoice = input("Enter 1 for 2.4GHz\nEnter 2 for 5.0GHz\n> ") 180 | if bandChoice == "1": 181 | band = "2.4GHz" 182 | arg_data.append(("rf-bands", band)) 183 | elif bandChoice == "2": 184 | band = "5.0GHz" 185 | arg_data.append(("rf-bands", band)) 186 | else: 187 | print("ERROR: You typed something else. Exiting...") 188 | sys.exit() 189 | 190 | nfc_data = gen_nfc_sub(arg_data) 191 | 192 | if verbose: 193 | for i in arg_data: 194 | print(i[0],i[1]) 195 | print_nfc_sub(nfc_data) 196 | else: 197 | with open(filename, mode='w') as f: 198 | write_nfc_sub(nfc_data) 199 | print("\nCreated " + filename) 200 | 201 | sys.exit() 202 | -------------------------------------------------------------------------------- /nfc_hexdump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Adds int and hex to RFID HEX dump 4 | [a 2 min script] 5 | 6 | nfc_hexdump.py file.nfc 7 | 8 | Written By: Peter Shipley github.com/evilpete 9 | 10 | From pkg https://github.com/evilpete/flipper_toolbox 11 | """ 12 | # In 13 | # Page 4: 03 29 91 01 14 | # Page 5: 15 55 04 79 15 | # Page 6: 6F 75 74 75 16 | # Page 7: 2E 62 65 2F 17 | 18 | # Out 19 | # Page 4: 03 29 91 01 # - ) - - 3 41 145 1 20 | # Page 5: 15 55 04 79 # - U - y 21 85 4 121 21 | # Page 6: 6F 75 74 75 # o u t u 111 117 116 117 22 | # Page 7: 2E 62 65 2F # . b e / 46 98 101 47 23 | 24 | import sys 25 | 26 | A = [] 27 | 28 | Chr = True # print as ascii char 29 | Dec = False # print as decimal 30 | Bin = True # print as binary 31 | Rev = False # reverse bit order 32 | 33 | av = sys.argv[1:] 34 | 35 | if av[0][0] == '-': 36 | Chr = Dec = Bin = Rev = False 37 | 38 | while av[0][0] == '-': 39 | arg = av.pop(0) 40 | 41 | if arg == '-r': 42 | Rev = True 43 | elif arg == '-b': 44 | Bin = True 45 | elif arg == '-d': 46 | Dec = True 47 | elif arg == '-c': 48 | Chr = True 49 | 50 | 51 | filename = av.pop(0) 52 | with open(filename, encoding="utf-8") as fd: 53 | header = fd.readline().strip() 54 | if header != 'Filetype: Flipper NFC device': 55 | print(f"Error: {filename} is not a 'Flipper NFC' sample file'") 56 | # sys.exit(1) 57 | for line in fd: 58 | a = line.split() 59 | if a[0] in ["Page", "Block"]: 60 | out_list = [] 61 | # b = [int(x, 16) for x in a[2:]] 62 | if Rev: 63 | b = [00 if x == '??' else int(f"{int(x, 16):08b}"[::-1], 2) for x in a[2:]] 64 | else: 65 | b = [00 if x == '??' else int(x, 16) for x in a[2:]] 66 | 67 | if Chr: 68 | # b = [int(a[2], 16), int(a[3], 16), int(a[4], 16), int(a[5], 16)] 69 | c = ["-" if x < 32 or x > 126 else chr(x) for x in b] 70 | d = " ".join(c) 71 | out_list.append(d) 72 | 73 | if Dec: 74 | e = [f"{x:3d}" for x in b] 75 | f = " ".join(e) + ' ' 76 | out_list.append(f) 77 | 78 | if Bin: 79 | # e = "{:3d} {:3d} {:3d} {:3d}".format(*b) 80 | g = " ".join([f"{x:08b}" for x in b]) 81 | out_list.append(g) 82 | 83 | # print(line.rstrip(), '# ', d, '\t', f, '\t', g) 84 | print(line.rstrip(), '#\t', ' '.join(out_list)) 85 | # print(e) 86 | # A.extend(e) 87 | else: 88 | print(line, end='') 89 | 90 | # print("".join(A)) 91 | -------------------------------------------------------------------------------- /nfc_prox2flip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Reads: proxmark3 MiFare json dump files 5 | Outputs: Flipper NFC compatable format 6 | 7 | nfc_prox2flip.py test_dat/mf-classic-1k-23AD7C86.json > mfc1k-23AD7C86.nfc 8 | 9 | Written By: Peter Shipley github.com/evilpete 10 | 11 | From pkg https://github.com/evilpete/flipper_toolbox 12 | """ 13 | 14 | import sys 15 | import time 16 | import json 17 | 18 | 19 | # Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card 20 | # ATQA SAK 21 | CARD_TYPE = { 22 | ("0400", "08"): "Mifare Classic", # 1k 23 | ("0200", "18"): "Mifare Classic", # 1k 24 | ("0400", "09"): "Mifare Mini", 25 | ("4400", "00"): "Mifare Ultralight", # "NTAG213" "NTAG216" 26 | ("4400", "20"): "Bank card", 27 | ("4403", "20"): "Mifare DESFire", 28 | } 29 | 30 | 31 | def convert_dat(in_dat): 32 | """ 33 | Take a parsed proxmark json dump arg 34 | returns list in Flipper NFC compatable format 35 | """ 36 | 37 | # output list 38 | out_dat = [] 39 | 40 | x = in_dat["Card"] 41 | 42 | # Guess card type by looking at ATQA/SAK combo 43 | j = (x["ATQA"], x["SAK"]) 44 | 45 | t = CARD_TYPE.get(j, x["UID"]) 46 | 47 | # this is a hack to generate Key maps 48 | # should add code to actually parse "SectorKeys" 49 | y = len(in_dat["SectorKeys"]) 50 | s = int("1" * y, 2) 51 | ska = skb = f"{s:016X}" 52 | 53 | out_dat.append( 54 | f""" 55 | Filetype: Flipper NFC device 56 | Version: 2 57 | # generated with flipper_toolbox 58 | # {time.ctime()} 59 | # Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card 60 | Device type: {t} 61 | # UID, ATQA and SAK are common for all formats 62 | UID: {x['UID'][0:2]} {x['UID'][2:4]} {x['UID'][4:6]} {x['UID'][6:8]} 63 | ATQA: {x['ATQA'][0:2]} {x['ATQA'][2:4]} 64 | SAK: {x['SAK']} 65 | # Mifare Classic specific data 66 | Mifare Classic type: 1K 67 | Data format version: 1 68 | # Key map is the bit mask indicating valid key in each sector 69 | Key A map: {ska} 70 | Key B map: {skb} 71 | # Mifare Classic blocks""" 72 | ) 73 | 74 | # Loop through blocks spliting data into 1 byte pieces 75 | for k, v in in_dat["blocks"].items(): 76 | b = " ".join([v[i: i + 2] for i in range(0, len(v), 2)]) 77 | out_dat.append(f"Block {k}: {b}") 78 | 79 | return out_dat 80 | 81 | 82 | if __name__ == "__main__": 83 | in_filename = "test_dat/mf-classic-1k-23AD7C86.json" 84 | 85 | if len(sys.argv) >= 2: 86 | in_filename = sys.argv[1] 87 | 88 | with open(in_filename, encoding="utf-8") as fd: 89 | input_dat = json.load(fd) 90 | 91 | output_list = convert_dat(input_dat) 92 | 93 | print("\n".join(output_list)) 94 | 95 | sys.exit() 96 | -------------------------------------------------------------------------------- /subghz/.fan-11T.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/subghz/.fan-11T.png -------------------------------------------------------------------------------- /subghz/.x10-unit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/subghz/.x10-unit.png -------------------------------------------------------------------------------- /subghz/README.md: -------------------------------------------------------------------------------- 1 | # Flipper Flipper SubGhz Files # 2 | 3 | A Collection of Signal Files 4 | 5 | --- 6 | 7 | 8 | #### [fan_bruteforce](fan_bruteforce) #### 9 | 10 | Brute Force RF code for Harbor Breeze Fan Remote Control 11 | Model FAN-11T FAN-35T FAN-53T 12 | 13 | Amazon product link: [Harbor Breeze Fan Remote Control](https://www.amazon.com/Ceiling-Control-Replacement-Hampton-KUJCE9103/) 14 | 15 | ( generated using [create_sub_dat.py](../create_sub_dat.py) ) 16 | 17 | --- 18 | 19 | 20 | 21 | #### X10 All ON / OFF (bruteforce) #### 22 | 23 | Send All-OFF or ALL-ON for every possible housecode (A -> P) 24 | 25 | [X10_All-OFF.sub](X10/X10_All-OFF.sub) : Send All-OFF for all housecodes 26 | 27 | [X10_All-ON.sub](X10/X10_All-ON.sub) : Send All-ON for all housecodes 28 | 29 | ( generated using [subghz_x10.py](../subghz_x10.py) ) 30 | 31 | --- 32 | 33 | 34 | -------------------------------------------------------------------------------- /subghz/X10/X10_All-LIGHTS-OFF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilpete/flipper_toolbox/c3b5fc19d0eacd7da4fff6f21f100b9b76446150/subghz/X10/X10_All-LIGHTS-OFF.png -------------------------------------------------------------------------------- /subghz_create_dat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Based heavily on jinschoi/create_sub.py 4 | # https://gist.github.com/jinschoi/f39dbd82e4e3d99d32ab6a9b8dfc2f55 5 | # 6 | # Peter Shipley github.com/evilpete 7 | # From pkg https://github.com/evilpete/flipper_toolbox 8 | # 9 | # Added: 10 | # Fan Control + Brute force pin 11 | # FSK support 12 | # 13 | 14 | import sys 15 | import os 16 | import time 17 | from typing import Iterable, Union, Any 18 | 19 | # pylint: disable=unspecified-encoding,too-many-arguments,too-many-locals,unused-argument 20 | 21 | # freq: frequency in Hz 22 | # zerolen: length of space bit in us 23 | # onelen: length of mark bit in us 24 | # repeats: number of times to repeat sequence 25 | # pause: time to wait in us between sequences 26 | # bits: string of ones and zeros to represent sequence 27 | 28 | _verbose = 0 29 | 30 | # listed in Firmware but not avalible (yet) 31 | # FuriHalSubGhzPresetMSK99_97KbAsync 32 | # FuriHalSubGhzPresetGFSK9_99KbAsync 33 | 34 | # Preset: FuriHalSubGhzPreset2FSKDev238Async 35 | # Preset: FuriHalSubGhzPreset2FSKDev476Async 36 | # Preset: FuriHalSubGhzPresetOok270Async 37 | # Preset: FuriHalSubGhzPresetOok650Async 38 | 39 | CommentText = "generated with flipper_toolbox" 40 | 41 | 42 | def gen_sub(freq, zerolen, onelen, repeats, pause, bits, modu='Ook', srate=650, comment_text=CommentText): 43 | 44 | res = f"""Filetype: Flipper SubGhz RAW File 45 | Version: 1 46 | # {comment_text} 47 | # {time.ctime()} 48 | Frequency: {freq} 49 | Preset: FuriHalSubGhzPreset{modu}{srate}Async 50 | Protocol: RAW 51 | """ 52 | 53 | zerolen_off = zerolen % 1 54 | onelen_off = onelen % 1 55 | delta_off = 0.0 56 | 57 | zerolen = int(zerolen) 58 | onelen = int(onelen) 59 | 60 | if pause == 0: 61 | # Pause must be non-zero. 62 | pause = zerolen 63 | 64 | data = [] 65 | prevbit = None 66 | prevbitlen = 0 67 | for bit in bits: 68 | if prevbit and prevbit != bit: 69 | data.append(prevbitlen) 70 | prevbitlen = 0 71 | 72 | if bit == '1': 73 | delta_off += onelen_off 74 | prevbitlen += onelen 75 | if delta_off > 1: 76 | prevbitlen += 1 77 | delta_off -= 1 78 | else: 79 | delta_off += zerolen_off 80 | prevbitlen -= zerolen 81 | if delta_off > 1: 82 | prevbitlen -= 1 83 | delta_off -= 1 84 | 85 | prevbit = bit 86 | 87 | if prevbit == '1': 88 | data.append(prevbitlen) 89 | data.append(-pause) 90 | else: 91 | data.append(prevbitlen - pause) 92 | 93 | # data = (data * repeats)[:-1] # Drop the last pause. 94 | datalines = [] 95 | for i in range(0, len(data), 512): 96 | batch = [str(n) for n in data[i:i + 512]] 97 | datalines.append(f'RAW_Data: {" ".join(batch)}') 98 | res += '\n'.join(datalines) 99 | 100 | return res 101 | 102 | 103 | # From Wikipedia 104 | def de_bruijn(k: Union[Iterable[Any], int], n: int) -> str: 105 | """de Bruijn sequence for alphabet k 106 | and subsequences of length n. 107 | """ 108 | # Two kinds of alphabet input: an integer expands 109 | # to a list of integers as the alphabet.. 110 | if isinstance(k, int): 111 | alphabet = list(map(str, range(k))) 112 | else: 113 | # While any sort of list becomes used as it is 114 | alphabet = k 115 | k = len(k) 116 | 117 | a = [0] * k * n 118 | sequence = [] 119 | 120 | def db(t, p): 121 | if t > n: 122 | if n % p == 0: 123 | sequence.extend(a[1:p + 1]) 124 | else: 125 | a[t] = a[t - p] 126 | db(t + 1, p) 127 | for j in range(a[t - p] + 1, k): 128 | a[t] = j 129 | db(t + 1, t) 130 | 131 | db(1, 1) 132 | return "".join(alphabet[i] for i in sequence) 133 | 134 | 135 | def debruijn(freq, zerolen, onelen, encoding, bitlen, alphabet=2): 136 | def encode(bit): 137 | return encoding[bit] 138 | return gen_sub(freq, zerolen, onelen, 1, 0, ''.join(encode(b) for b in de_bruijn(alphabet, bitlen))) 139 | 140 | 141 | def gen_opensesame(): 142 | # https://github.com/samyk/opensesame/blob/48b7d25c9d7aa3e2ac5cadfdcb2db1c78e001565/garages.h 143 | # 300000000, 310000000, 315000000, 318000000, 390000000, 433920000 144 | 145 | for hz in [300000000, 310000000]: 146 | with open(f'10bit-{hz//1000000}mhz.sub', 'w') as f: 147 | print(debruijn(hz, 500, 500, {'0': '1000', '1': '1110'}, 10), file=f) 148 | 149 | for hz in [315000000, 390000000]: 150 | with open(f'9bit-{hz//1000000}mhz.sub', 'w') as f: 151 | print(debruijn(hz, 500, 500, {'0': '1000', '1': '1110'}, 9), file=f) 152 | 153 | for hz in [318000000, 433920000]: 154 | with open(f'nscd-{hz//1000000}.sub', 'w') as f: 155 | print(debruijn(hz, 500, 500, {'0': '100000000100000000', 156 | '1': '111111110100000000', 157 | '2': '111111110111111110'}, 9, alphabet=3), file=f) 158 | 159 | for hz in [300000000, 310000000, 315000000, 318000000, 390000000, 433920000]: 160 | with open(f'12bit-{hz//1000000}mhz.sub', 'w') as f: 161 | print(debruijn(hz, 500, 500, {'0': '1000', '1': '1110'}, 12), file=f) 162 | 163 | 164 | # pylint: disable=line-too-long 165 | def gen_tesla(): 166 | # https://github.com/jimilinuxguy/Tesla-Charging-Port-Opener 167 | with open('tesla.sub', 'w') as f: 168 | print(gen_sub(315000000, 400, 400, 10, 25562, '101010101010101010101010100010101100101100110010110011001100110011001011010011010010110101001010110100110100110010101011010010110001010110010110011001011001100110011001100101101001101001011010100101011010011010011001010101101001011000101011001011001100101100110011001100110010110100110100101101010010101101001101001100101010110100101'), file=f) 169 | 170 | 171 | # see Also https://github.com/merbanan/rtl_433/blob/master/conf/fan-11t.conf 172 | 173 | # 01 + 4 bit ID + 0 + 6 bit command 174 | fan_comm = { 175 | 'High': '100000', 176 | 'Med': '010000', 177 | 'Low': '001000', 178 | # '???': '000100', # not used ? 179 | 'Off': '000010', 180 | 'Lit': '000001', 181 | } 182 | fan_end = '000000' 183 | fan_freq = 302500000 184 | 185 | # DRATE 3015 186 | # (1/3015) * 1000000 = ~333us 187 | fan_bit_len = 333 188 | 189 | 190 | def gen_fan_cmd(pin="1010"): 191 | """ 192 | FAN-11T Remote Control of Harbor Breeze Fan 193 | """ 194 | 195 | # Fan used PWM, to transmit as OOK we pad out the 1's and 0's 196 | # PWM into OOK 197 | # 1 = 011 198 | # 0 = 001 199 | fan_comm_end = ''.join(['011' if b == '1' else '001' for b in f"01{pin}0{fan_end}"]) 200 | fan_space = '0' * 39 201 | 202 | if _verbose: 203 | print('end', fan_end) 204 | print('fan_end cmd_pwm', fan_comm_end) 205 | 206 | for k, v in fan_comm.items(): 207 | 208 | f_cmd = ''.join(['011' if b == '1' else '001' for b in f"01{pin}0{v}"]) 209 | 210 | # freq, zerolen, onelen, repeats, pause, bits 211 | # cmd_pwm = f'{fan_space}{f_cmd}' 212 | # full_pwm = (cmd_pwm * 5 + fan_space + fan_comm_end + fan_space) * 2 213 | cmd_pwm = f'{f_cmd}{fan_space}' 214 | full_pwm = (cmd_pwm * 5 + fan_comm_end + fan_space) * 2 215 | # print('fan_{}.sub'.format(k), fan_comm[k]) 216 | if _verbose: 217 | print(k, v) 218 | print(f'fan_{k} cmd_pwm', cmd_pwm) 219 | # print("xmit us:", len(full_pwm) * fan_bit_len) 220 | # print(f'fan_{k} full_pwm', full_pwm) 221 | 222 | with open(f'fan_{k}-{pin}.sub', 'w') as f: 223 | # gen_sub(freq, zerolen, onelen, repeats, pause, bits) 224 | print(gen_sub(fan_freq, fan_bit_len, fan_bit_len, 1, 0, full_pwm), file=f) 225 | 226 | 227 | def gen_fan_brute(): 228 | """ 229 | FAN-11T Remote Control of Harbor Breeze Fan 230 | 231 | Brute Force 4 bit pin code 232 | """ 233 | 234 | fan_space = "0" * 39 235 | 236 | # Fan used PWM, to transmit as OOK we pad out the 1's and 0's 237 | # PWM into OOK 238 | # 1 = 011 239 | # 0 = 001 240 | 241 | # if _verbose: 242 | # print('end', fan_end) 243 | # print('fan_end cmd_pwm', fan_comm_end) 244 | 245 | for k, v in fan_comm.items(): 246 | 247 | pwm_dat = [] 248 | 249 | for p in range(16): 250 | 251 | pin = f"{p:04b}" 252 | 253 | f_cmd = ''.join(['011' if b == '1' else '001' for b in f"01{pin}0{v}"]) 254 | fan_cmd_end = ''.join(['011' if b == '1' else '001' for b in f"01{pin}0{fan_end}"]) 255 | 256 | cmd_pwm = f'{f_cmd}{fan_space}' 257 | full_pwm = (cmd_pwm * 4 + fan_cmd_end + fan_space) * 2 258 | 259 | pwm_dat.append(full_pwm) 260 | 261 | if _verbose: 262 | print(k, pin, v) 263 | print(f'fan_{k} cmd_pwm', cmd_pwm) 264 | # print(f'fan_{k} full_pwm', full_pwm) 265 | 266 | if _verbose: 267 | print(k, "xmit us:", len("".join(pwm_dat)) * fan_bit_len) 268 | 269 | with open(f'fan_brute-{k}.sub', 'w') as f: 270 | # gen_sub(freq, zerolen, onelen, repeats, pause, bits) 271 | print(gen_sub(fan_freq, fan_bit_len, fan_bit_len, 1, 0, "".join(pwm_dat), comment_text=f"FAN-11T Remote Control {k}"), file=f) 272 | 273 | 274 | TOUCH_TUNES_COMMANDS = { 275 | 'On_Off': 0x78, 276 | 'Pause': 0x32, # 0xB3, 277 | 'P1': 0x70, # 0xF1, 278 | 'P2_Edit_Queue': 0x60, 279 | 'P3_Skip': 0xCA, 280 | 'F1_Restart': 0x20, 281 | 'F2_Key': 0xA0, 282 | 'F3_Mic_A_Mute': 0x30, 283 | 'F4_Mic_B_Mute': 0xB0, 284 | 'Mic_Vol_Plus_Up_Arrow': 0xF2, 285 | 'Mic_Vol_Minus_Down_Arrow': 0x80, 286 | 'A_Left_Arrow': 0x84, 287 | 'B_Right_Arrow': 0xC4, 288 | 'OK': 0x44, # 0xDD, 289 | 'Music_Vol_Zone_1Up': 0xD0, # 0xF4, 290 | 'Music_Vol_Zone_1Down': 0x50, 291 | 'Music_Vol_Zone_2Up': 0x90, # 0xF6, 292 | 'Music_Vol_Zone_2Down': 0x10, 293 | 'Music_Vol_Zone_3Up': 0xC0, # 0xFC, 294 | 'Music_Vol_Zone_3Down': 0x40, 295 | '1': 0xF0, 296 | '2': 0x08, 297 | '3': 0x88, 298 | '4': 0x48, 299 | '5': 0xC8, 300 | '6': 0x28, 301 | '7': 0xA8, 302 | '8': 0x68, 303 | '9': 0xE8, 304 | '0': 0x98, 305 | 'Music_Karaoke(star)': 0x18, 306 | 'Lock_Queue(#)': 0x58 307 | } 308 | 309 | 310 | def encode_touchtunes(command, pin=0x00): 311 | # Syncword 312 | frame = 0x5D 313 | 314 | # PIN 315 | for bit in range(8): 316 | frame <<= 1 317 | if pin & (1 << bit): 318 | frame |= 1 319 | # Insert button code and it's complement 320 | frame <<= 16 321 | frame |= (command << 8) 322 | frame |= (command ^ 0xFF) 323 | 324 | # Convert to raw signal 325 | # 0 symble == 10 && 1 symble == 1000 326 | ook = "" 327 | for _i in range(8 + 8 + 16): 328 | if frame & 0x80000000: 329 | ook += "1000" 330 | frame <<= 1 331 | else: 332 | ook += "10" 333 | frame <<= 1 334 | return "1" * 16 + "0" * 8 + ook + "1000" 335 | 336 | 337 | # Touch Tunes jukebox (https://github.com/notpike/The-Fonz/blob/master/The_Fonz.py) 338 | def gen_touch_tunes(pin=0): 339 | # pin 0->255 340 | 341 | dirname = f"touch_tunes-{pin:03d}" 342 | if not os.path.isdir(dirname): 343 | os.mkdir(dirname) 344 | 345 | # Touch Tunes jukebox (https://github.com/notpike/The-Fonz/blob/master/The_Fonz.py) 346 | for cmdname, cmd in TOUCH_TUNES_COMMANDS.items(): 347 | with open(dirname + '/' + cmdname + '.sub', 'w') as f: 348 | print(gen_sub(433920000, 566, 566, 1, 0, encode_touchtunes(cmd, pin)), file=f) 349 | 350 | 351 | if __name__ == '__main__': 352 | 353 | targ = "all" 354 | args = sys.argv[1:] 355 | 356 | if args: 357 | targ = args.pop(0) 358 | 359 | # Garage Door opener 360 | if targ in ["all", "opensesame"]: 361 | gen_opensesame() 362 | 363 | # Tesla-Charging-Port-Opener 364 | if targ in ["all", "tesla"]: 365 | gen_tesla() 366 | 367 | # Touch Tunes jukebox 368 | if targ in ["all", "ttones"]: 369 | gen_touch_tunes() 370 | 371 | # FAN-11T Remote Control of Harbor Breeze Fan ( with pin ) 372 | # gen_fan_cmd("1010") 373 | 374 | # FAN-11T Remote Control of Harbor Breeze Fan 375 | # Brute Force Pin 376 | if targ in ["all", "fan"]: 377 | gen_fan_brute() 378 | 379 | sys.exit() 380 | -------------------------------------------------------------------------------- /subghz_gen_cmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # 4 | # A command line bases generator for Flipper SubGhz RAW File 5 | # 6 | # Peter Shipley github.com/evilpete 7 | # 8 | # From pkg https://github.com/evilpete/flipper_toolbox 9 | # 10 | # Based heavily on jinschoi/create_sub.py 11 | # 12 | # https://gist.github.com/jinschoi/f39dbd82e4e3d99d32ab6a9b8dfc2f55 13 | # 14 | # 15 | 16 | import sys 17 | import time 18 | # import os 19 | # from typing import Iterable, Union, Any 20 | import argparse 21 | 22 | # pylint: disable=unspecified-encoding, too-many-arguments, too-many-locals, unused-argument 23 | 24 | _verbose = 0 25 | 26 | # Preset: FuriHalSubGhzPreset2FSKDev238Async 27 | # Preset: FuriHalSubGhzPreset2FSKDev476Async 28 | # Preset: FuriHalSubGhzPresetOok270Async 29 | # Preset: FuriHalSubGhzPresetOok650Async 30 | # listed in Firmware but not avalible (yet) 31 | # FuriHalSubGhzPresetMSK99_97KbAsync 32 | # FuriHalSubGhzPresetGFSK9_99KbAsync 33 | 34 | 35 | def gen_sub(freq, zerolen, onelen, baudrate=None, pause=0, bits="", modu='Ook', modopt=650): 36 | """generate Flipper SubGhz RAW data 37 | 38 | Parameters 39 | ---------- 40 | int : freq 41 | frequency in Hz 42 | int : zerolen 43 | length of space bit in us 44 | int ; onelen 45 | length of mark bit in us 46 | int : baud 47 | baud rate, is given options zerolen and onelen are ignored 48 | int : repeats 49 | number of times to repeat sequence 50 | int : pause 51 | time to wait in us between sequence, defaults to value zerolen 52 | str : bits 53 | string of ones and zeros to represent sequence 54 | str : modu 55 | modulation, valid values 2FSK or Ook, default="Ook" 56 | str : modopt 57 | modulation option, valid values: 58 | Ook: '270' or '650' 59 | 2FSK: Dev238' or 'Dev476 60 | 61 | Returns 62 | ------- 63 | str 64 | binary data in string form 65 | 66 | """ 67 | 68 | res = f"""Filetype: Flipper SubGhz RAW File 69 | Version: 1 70 | Frequency: {freq} 71 | # {time.ctime()} 72 | # Generated with https://github.com/evilpete/flipper_toolbox 73 | Preset: FuriHalSubGhzPreset{modu}{modopt}Async 74 | Protocol: RAW 75 | """ 76 | 77 | if baudrate is not None: 78 | zerolen = onelen = (1 / baudrate) * 1000000 79 | 80 | # if modu not in ['Ook', "2FSK"]: 81 | # raise ValueError("modu value can only be 'Ook' or '2FSK'") 82 | # 83 | # if modu == 'Ook' and modopt not in ['270', '650']: 84 | # raise ValueError("Ook: modopt value can only be '270' or '650'") 85 | # 86 | # if modu == '2FSK' and modopt not in ['Dev238', 'Dev476']: 87 | # raise ValueError("2FSK: modopt value can only be 'Dev238' or 'Dev476'") 88 | 89 | zerolen_off = zerolen % 1 90 | onelen_off = onelen % 1 91 | delta_off = 0.0 92 | 93 | zerolen = int(zerolen) 94 | onelen = int(onelen) 95 | 96 | # if _verbose: 97 | # print( f"zerolen={zerolen}, onelen={onelen}, baudrate={baudrate}, " 98 | # f"zerolen_off={zerolen_off:0.04f}, onelen_off={onelen_off:0.03f}" ) 99 | 100 | if pause == 0: 101 | # Pause must be non-zero. 102 | pause = zerolen 103 | 104 | data = [] 105 | prevbit = None 106 | prevbitlen = 0 107 | for bit in bits: 108 | if prevbit and prevbit != bit: 109 | data.append(prevbitlen) 110 | prevbitlen = 0 111 | 112 | if bit == '1': 113 | delta_off += onelen_off 114 | prevbitlen += onelen 115 | if delta_off > 1: 116 | prevbitlen += 1 117 | delta_off -= 1 118 | else: 119 | delta_off += zerolen_off 120 | prevbitlen -= zerolen 121 | if delta_off > 1: 122 | prevbitlen -= 1 123 | delta_off -= 1 124 | 125 | prevbit = bit 126 | 127 | if prevbit == '1': 128 | data.append(prevbitlen) 129 | data.append(-pause) 130 | else: 131 | data.append(prevbitlen - pause) 132 | 133 | # data = (data * repeats)[:-1] # Drop the last pause. 134 | datalines = [] 135 | for i in range(0, len(data), 512): 136 | batch = [str(n) for n in data[i:i + 512]] 137 | datalines.append(f'RAW_Data: {" ".join(batch)}') 138 | 139 | res += '\n'.join(datalines) 140 | 141 | if _verbose > 1: 142 | print(f"delta_off {delta_off}") 143 | 144 | return res 145 | 146 | 147 | def hex2bin(s): 148 | """Convert strings of Hedecimal data into binary strings 149 | 150 | Parameters 151 | ---------- 152 | str 153 | hexadecimal data in string form 154 | 155 | Returns 156 | ------- 157 | str 158 | binary data in string form 159 | 160 | """ 161 | 162 | r = [] 163 | if s[:2] in ["0x", "OX"]: 164 | s = s[2:] 165 | sl = len(s) 166 | # if (sl % 2): 167 | # s += '0' 168 | for i in range(0, sl, 1): 169 | b = "{:04b}".format(int(s[i], 16)) # pylint: disable=consider-using-f-string 170 | # print(s[i], b) 171 | r.append(b) 172 | return ''.join(r) 173 | 174 | 175 | Modulation_Presets = { 176 | '2FSKDev238': ('2FSK', 'Dev238'), # FuriHalSubGhzPreset2FSKDev238Async 177 | '2FSKDev476': ('2FSK', 'Dev476'), # FuriHalSubGhzPreset2FSKDev476Async 178 | 'Ook270': ('Ook', '270'), # FuriHalSubGhzPresetOok270Async 179 | 'Ook650': ('Ook', '650'), # FuriHalSubGhzPresetOok650Async 180 | } 181 | 182 | 183 | def arg_line(): 184 | """ Parse command line args. 185 | 186 | Parameters 187 | ---------- 188 | None 189 | 190 | Returns 191 | ------- 192 | argparse Namespace obj 193 | 194 | """ 195 | # pylint: disable=global-statement 196 | global _verbose 197 | 198 | epilog = f''' 199 | example:\n\t {sys.argv[0]} -f 302500000 -0 333 -1 333 -m -B 0110100001000 200 | \n\t {sys.argv[0]} -f 302500000 -b 3015 -m -H 0x6840 -p Ook650 201 | 202 | If baud is given options zerolen and onelen are ignored 203 | 204 | Modulation presets: 205 | 2FSKDev238: FuriHalSubGhzPreset2FSKDev238Async 206 | 2FSKDev476: FuriHalSubGhzPreset2FSKDev476Async 207 | Ook270: FuriHalSubGhzPresetOok270Async 208 | Ook650: FuriHalSubGhzPresetOok650Async 209 | ''' 210 | 211 | parser = argparse.ArgumentParser(add_help=True, # noqa 212 | epilog=epilog, 213 | formatter_class=argparse.RawDescriptionHelpFormatter 214 | ) 215 | 216 | parser.add_argument('-v', '--verbose', dest="verb", 217 | default=0, 218 | help='Increase debug verbosity', action='count') 219 | 220 | parser.add_argument("-o", "--outfile", dest="out_file", 221 | default="subghz.sub", 222 | help="Output filename") 223 | 224 | parser.add_argument("-f", "--freq", dest="send_freq", 225 | type=int, 226 | default=433920000, 227 | help="Transmit frequency") 228 | 229 | parser.add_argument("-0", "--zerolen", dest="zero_len", 230 | type=int, 231 | default=None, 232 | help="Length of space bit in ms") 233 | 234 | parser.add_argument("-1", "--onelen", dest="one_len", 235 | type=int, 236 | default=None, 237 | help="Length of mark bit in ms") 238 | 239 | parser.add_argument("-i", "--invert", dest="invert", 240 | action='store_true', 241 | help="Invert bits") 242 | 243 | parser.add_argument("-b", "--baud", dest="baud_rate", 244 | type=int, 245 | default=None, 246 | help="data baud rate") 247 | 248 | data_grp = parser.add_mutually_exclusive_group(required=True) 249 | 250 | data_grp.add_argument("-H", "--Hex", dest="hex_data", 251 | default=None, 252 | help="Packet data in hex") 253 | 254 | data_grp.add_argument("-B", "--Binary", dest="bin_data", 255 | default='None', 256 | help="Packet data as string of ones and zeros") 257 | 258 | # Preset: FuriHalSubGhzPreset2FSKDev238Async 259 | # Preset: FuriHalSubGhzPreset2FSKDev476Async 260 | # Preset: FuriHalSubGhzPresetOok270Async 261 | # Preset: FuriHalSubGhzPresetOok650Async 262 | parser.add_argument("-p", "--preset", dest="mod_preset", 263 | default='Ook650', 264 | help="Modulation preset") 265 | 266 | parser.add_argument("-m", "--manchester", dest="manch_encode", 267 | action='store_true', 268 | help="manchester encoded") 269 | 270 | parser.add_argument("-r", "--repeat", dest="repeat_cnt", 271 | type=int, default=1, 272 | help="number of times to repeat sequence") 273 | 274 | parser.add_argument("-d", "--delay", dest="delay_padding", 275 | type=int, default=1, 276 | help="delay padding between repeated sequences") 277 | 278 | args_data, _unknown_args = parser.parse_known_args() 279 | 280 | if args_data.verb: 281 | _verbose += args_data.verb 282 | 283 | if _verbose: 284 | print(f"\nargs: {args_data}\n") 285 | 286 | return args_data 287 | 288 | 289 | def main(): 290 | 291 | args = arg_line() 292 | zero_len = one_len = None 293 | 294 | if args.baud_rate is None: 295 | if args.zero_len and args.one_len: 296 | zero_len = args.zero_len 297 | one_len = args.one_len 298 | elif args.zero_len or args.one_len: 299 | zero_len = one_len = args.zero_len or args.one_len 300 | else: 301 | print("Error: Bit Length or Baudrate must be given") 302 | print("\tuse --help opton for more info\n") 303 | # parser.print_help() 304 | sys.exit(1) 305 | 306 | mod_settings = ('Ook', '650') 307 | if args.mod_preset in Modulation_Presets: 308 | mod_settings = Modulation_Presets[args.mod_preset] 309 | 310 | if args.hex_data: 311 | bin_data = hex2bin(args.hex_data) 312 | if _verbose: 313 | print(f"hex_data: {args.hex_data}") 314 | else: 315 | bin_data = args.bin_data 316 | 317 | # print(f"bin_data: {bin_data}") 318 | 319 | if args.manch_encode: 320 | bin_data = ''.join(['10' if b == '1' else '01' for b in bin_data]) 321 | 322 | if args.invert: 323 | bin_data = ''.join(['0' if b == '1' else '1' for b in bin_data]) 324 | 325 | if _verbose: 326 | print(f"bin_data: {bin_data}") 327 | 328 | packet_data = gen_sub(args.send_freq, 329 | zero_len, one_len, args.baud_rate, 0, 330 | bin_data, 331 | mod_settings[0], mod_settings[1]) 332 | 333 | if _verbose > 1: 334 | print(f"packet_data: {packet_data}") 335 | 336 | with open(args.out_file, 'w', encoding="utf-8") as fd: 337 | print(packet_data, file=fd) 338 | 339 | sys.exit() 340 | 341 | 342 | if __name__ == '__main__': 343 | main() 344 | -------------------------------------------------------------------------------- /subghz_histogram.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Peter Shipley github.com/evilpete 4 | 5 | Script to read Flipper SubGhz RAW File and plot 0 & 1 segment lengths using pyplot 6 | 7 | From pkg https://github.com/evilpete/flipper_toolbox 8 | 9 | Based heavily on jinscho's gist : 10 | https://gist.github.com/jinschoi/8396f25a4cb7ac7986a7d881026ae950 11 | """ 12 | 13 | import re 14 | import sys 15 | from statistics import mean, median_high 16 | import pandas as pd 17 | import matplotlib.pyplot as plt 18 | 19 | # LIMIT = 1000 # this number is arbitrary 20 | LIMIT = None # If None, limit will be calculated from the median 21 | 22 | _debug = 0 23 | 24 | filename = sys.argv[1] 25 | 26 | psegs = [] 27 | nsegs = [] 28 | with open(filename, 'r', encoding="utf-8") as fd: 29 | header = fd.readline().strip() 30 | if header != 'Filetype: Flipper SubGhz RAW File': 31 | print(f"Error: {filename} is not a 'Flipper SubGhz RAW File' sample file'") 32 | sys.exit(1) 33 | 34 | for line in fd: 35 | m = re.match(r'RAW_Data:\s*([-0-9 ]+)\s*$', line) 36 | if m: 37 | nsegs.extend(abs(int(seg)) for seg in m[1].split(r' ') if int(seg) < 0) 38 | psegs.extend(abs(int(seg)) for seg in m[1].split(r' ') if int(seg) > 0) 39 | 40 | if _debug: 41 | print("nseg :", min(nsegs), mean(nsegs), max(nsegs)) 42 | print("pseg :", min(psegs), mean(psegs), max(psegs)) 43 | 44 | 45 | limit = LIMIT 46 | if limit is None: 47 | data_median = max(median_high(nsegs), median_high(psegs)) 48 | limit = 4 * data_median 49 | print(f"Limit = median * 4 = {data_median} * 4 = {limit}") 50 | # max_dat = max(nsegs + psegs) 51 | # print("max =", max_dat) 52 | 53 | 54 | pseries = pd.Series(data=psegs) 55 | nseries = pd.Series(data=nsegs) 56 | 57 | pseries = pseries[pseries < limit] 58 | nseries = nseries[nseries < limit] 59 | 60 | df = pd.DataFrame(pseries, columns=['pos']) 61 | df['neg'] = nseries 62 | 63 | ax = df.plot.hist(bins=int(limit / 4), 64 | log=False, 65 | alpha=0.5, figsize=(6, 3), 66 | title='Histogram of segment length') 67 | 68 | ax.set(xlabel='milliseconds') 69 | 70 | ax.grid(True, which='major', axis='y') 71 | 72 | plt.show() 73 | -------------------------------------------------------------------------------- /subghz_insteon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | subghz_insteon.py : Generate Insteon command packets in Flipper .sub format 4 | 5 | Written By: Peter Shipley github.com/evilpete 6 | 7 | From pkg https://github.com/evilpete/flipper_toolbox 8 | """ 9 | 10 | import sys 11 | import time 12 | # import argparse 13 | # import string 14 | # import pprint 15 | 16 | # 17 | # Generate Insteon command packets in Flipper .sub format 18 | # 19 | # Peter Shipley github.com/evilpete 20 | # 21 | # From pkg https://github.com/evilpete/flipper_toolbox 22 | # 23 | 24 | # Usage; 25 | # ./subghz_insteon.py [On|Off] 26 | # 27 | # Example: 28 | # ./subghz_insteon.py 163FE5 132580 Off > device_off.sub 29 | # 30 | 31 | 32 | # Note: 33 | # 34 | # an insteon switch needs to be "paired" before it will accept command from 35 | # andother device, but there is no authenticaion or encryption. 36 | # 37 | # the easiest way to get the insteon node id/address of a pair is to run rtl_433 38 | # 39 | # rtl_433 -f 914.8M -s 2048k -R 159 -Y classic 40 | # 41 | # rtl_433 output: 42 | # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 43 | # time : 2022-11-28 21:36:45 44 | # model : Insteon From_Addr : 4C1B63 To_Addr : 347864 Message_Type: 0 45 | # Message_Str: Direct Message Extended : 0 Hops_Max : 3 46 | # Hops_Left : 0 Packet : 03 : 247864 : 4C1B61 : 13 00 BE Integrity : CRC 47 | # Payload : 03647824611B4C1300BE00 48 | 49 | # the run the command : 50 | # 51 | # ./subghz_insteon.py 4C1B63 347864 Off > office_light_off.sub 52 | # 53 | 54 | # Insteon Packet encoding format : 55 | # 56 | # Packet encoding example 57 | # 58 | # The short Packet Fields are 59 | # Flags = byte 0 60 | # To Addr = byte 1 2 3 61 | # From Addr = byte 4 5 6 62 | # Command = byte 7 63 | # Cmd Arg = byte 8 64 | # Pkt CRC = byte 9 65 | # pad 00 = byte 10 11 ( optional ) 66 | # pad AA = byte 12 ( optional ) 67 | # 68 | 69 | _debug = 0 70 | 71 | lsfr_table = [0x00, 0x30, 0x60, 0x50, # 0 1 2 3 72 | 0xC0, 0xF0, 0xA0, 0x90, # 4 5 6 7 73 | 0x80, 0xB0, 0xE0, 0xD0, # 8 9 A B 74 | 0x40, 0x70, 0x20, 0x10] # C D E F 75 | 76 | cmd_table = { 77 | "ON": (0x11, 255), 78 | "FASTON": (0x12, None), 79 | "OFF": (0x13, None), 80 | "FASTOFF": (0x14, None), 81 | 'BRIGHTEN': (0x15, None), 82 | "BRT": (0x15, None), 83 | "DIM": (0x16, None), 84 | "FADEDOWN": (0x17, 0), 85 | "FADEUP": (0x17, 1), 86 | "STOP": (0x18, 0), 87 | "FADESTOP": (0x18, 0), 88 | "BEEP": (0x30, None), 89 | "PING": (0x0F, None), 90 | } 91 | 92 | 93 | def pkt_crc(dat): 94 | """ 95 | calc packet CRC 96 | 97 | takes an instion packet in form of a list of ints 98 | and returns the CRC for RF packet 99 | 100 | This uses a table lookup to effectivly doing: 101 | r ^= dat[i] ; 102 | r ^= (( r ^ ( r << 1 )) & 0x0F) << 4 ; 103 | 104 | """ 105 | 106 | r = 0 107 | for i in dat[:9]: 108 | r ^= i 109 | r ^= lsfr_table[r & 0x0F] 110 | 111 | return r 112 | 113 | 114 | def percent_to_byte(p_str, def_val=255): 115 | if p_str.isdigit(): 116 | p = int(p_str) 117 | r = int(p * 255 / 100) 118 | return min(r, 255) 119 | 120 | return def_val 121 | 122 | 123 | # takes a list representig a insteon rf command bytes / payload 124 | # and generates a rf binary in the form of a string 125 | def insteon_encode(b_list, repeat=3): 126 | # l = len(b_list) 127 | 128 | padding = ''.join(['10' if b == '1' else '01' for b in "0101" * 13]) 129 | 130 | aa = ''.join(['10' if b == '1' else '01' for b in "01010101"]) 131 | blks = [aa] 132 | i = 0 133 | for x in b_list: 134 | if i == 0: 135 | ix = 31 136 | else: 137 | ix = 12 - i 138 | i += 1 139 | 140 | d = x 141 | 142 | ibr = f"{ix:05b}"[::-1] 143 | dbr = f"{d:08b}"[::-1] 144 | 145 | if _debug > 1: 146 | print("00", ibr, dbr, " : ", ix, f"{x:02X}", file=sys.stderr) 147 | 148 | # ib = f"{ix:05b}" 149 | # db = f"{d:08b}" 150 | # print(ix, cmd_hex[x:x+2], ib, db, '->', ibr, dbr) 151 | 152 | md = ''.join(['10' if b == '1' else '01' for b in f"{ibr}{dbr}"]) 153 | if _debug > 1: 154 | print("md=", md, file=sys.stderr) 155 | blks.append('00' + md) 156 | 157 | inst_pkt = ''.join(blks) 158 | 159 | ret_list = [inst_pkt] 160 | 161 | for i in range(1, repeat): 162 | ret_list.extend((padding, inst_pkt)) 163 | 164 | # print(ret_list) 165 | return ret_list 166 | 167 | 168 | hex_set = set('abcdefABCDEF0123456789') 169 | 170 | 171 | def is_hex_str(s): 172 | return set(s).issubset(hex_set) 173 | 174 | 175 | # takes a list representig a insteon rf command bytes 176 | def gen_insteon_pkt(): 177 | 178 | pkt_list = [0x0F] 179 | 180 | args = sys.argv[1:] 181 | 182 | if _debug > 1: 183 | print("args", args, file=sys.stderr) 184 | 185 | if len(args) < 3: 186 | print("requires three or more args") 187 | sys.exit() 188 | 189 | # dest addr 190 | addr = args.pop(0) 191 | 192 | a = [addr[4:6], addr[2:4], addr[0:2]] 193 | # pkt_list.extend([int(x, 16) for x in a]) 194 | pkt_list.extend(map(lambda x: int(x, 16), a)) 195 | 196 | # src addr 197 | addr = args.pop(0) 198 | 199 | if addr.startswith('0000'): 200 | pkt_list[0] = 0xCF 201 | 202 | a = [addr[4:6], addr[2:4], addr[0:2]] 203 | pkt_list.extend(map(lambda x: int(x, 16), a)) 204 | # pkt_list.extend([int(x, 16) for x in a]) 205 | 206 | cmd = args.pop(0) 207 | cmd_arg = None 208 | 209 | if cmd.upper() in cmd_table: 210 | c1, cmd_arg = cmd_table[cmd.upper()] 211 | pkt_list.append(c1) 212 | elif is_hex_str(cmd): 213 | pkt_list.append(int(cmd, 16) & 0xff) 214 | else: 215 | print(f"unknown cmd value '{cmd}'") 216 | print("valid commands are:'") 217 | print("\t", " ".join(cmd_table.keys())) 218 | sys.exit() 219 | 220 | if args: 221 | arg = args.pop(0) 222 | if is_hex_str(arg): 223 | val = int(arg, 16) 224 | pkt_list.append(val) 225 | # val = percent_to_byte(arg[1:]) 226 | else: 227 | print(f"unknown arg value '{arg}'") 228 | sys.exit() 229 | elif cmd_arg is not None: 230 | pkt_list.append(cmd_arg) 231 | else: 232 | pkt_list.append(0) 233 | 234 | p_crc = pkt_crc(pkt_list) 235 | 236 | pkt_list.extend([p_crc, 0, 0, 0xAA]) 237 | 238 | if _debug > 1: 239 | print("pkt_list", pkt_list, file=sys.stderr) 240 | # hex_str_list = [f"{x:02X}" for x in pkt_list] 241 | hex_str_list = list(map(lambda x: f"{x:02X}", pkt_list)) 242 | print(hex_str_list, file=sys.stderr) 243 | print("".join(hex_str_list), file=sys.stderr) 244 | 245 | return pkt_list 246 | 247 | 248 | # takes a rf binary in the form of a string 249 | # and generates a Flipper SubGhz encoded file 250 | def print_subfile(pkt_list, note="Insteon Command"): 251 | 252 | pkt_bit_len = 109.2 253 | 254 | bit_len = int(pkt_bit_len) 255 | bit_len_off = pkt_bit_len % 1 256 | delta_off = 0.0 257 | 258 | data_list = [] 259 | for pkt_bits in pkt_list: 260 | 261 | data = [] 262 | prevbit = None 263 | prevbitlen = 0 264 | 265 | for bit in pkt_bits: 266 | if prevbit and prevbit != bit: 267 | data.append(prevbitlen) 268 | prevbitlen = 0 269 | 270 | if bit == '1': 271 | delta_off += bit_len_off 272 | prevbitlen += bit_len 273 | if delta_off > 1: 274 | prevbitlen += 1 275 | delta_off -= 1 276 | else: 277 | delta_off += bit_len_off 278 | prevbitlen -= bit_len 279 | if delta_off > 1: 280 | prevbitlen -= 1 281 | delta_off -= 1 282 | 283 | prevbit = bit 284 | 285 | data.append(prevbitlen) 286 | 287 | data_list.append(data) 288 | 289 | hdr = f"""Filetype: Flipper SubGhz RAW File 290 | Version: 1 291 | # {note} 292 | # Generated with subghz_insteon.py https://github.com/evilpete/flipper_toolbox 293 | # {time.ctime()} 294 | Frequency: 915000000 295 | Preset: FuriHalSubGhzPreset2FSKDev476Async 296 | Protocol: RAW 297 | """ 298 | 299 | res = hdr 300 | datalines = [] 301 | for data in data_list: 302 | for i in range(0, len(data), 512): 303 | # batch = [str(n) for n in data[i:i + 512]] 304 | batch = map(str, data[i:i + 512]) 305 | datalines.append(f'RAW_Data: {" ".join(batch)}') 306 | 307 | res += '\n'.join(datalines) 308 | 309 | return res 310 | 311 | 312 | if __name__ == '__main__': 313 | 314 | p_list = gen_insteon_pkt() 315 | 316 | hexstr = ' '.join([f"{x:02X}" for x in p_list]) 317 | 318 | if _debug: 319 | print("p_list", p_list, file=sys.stderr) 320 | # print([f"{x:02X}" for x in p_list], file=sys.stderr) 321 | print(hexstr, file=sys.stderr) 322 | 323 | pkt_data = insteon_encode(p_list) 324 | 325 | if _debug > 1: 326 | print("pkt_data", pkt_data, file=sys.stderr) 327 | 328 | file_comment = "Insteon command : " + \ 329 | ' '.join(sys.argv[1:]).upper() + " : " + hexstr 330 | 331 | fdata = print_subfile(pkt_data, note=file_comment) 332 | 333 | print(fdata) 334 | 335 | sys.exit() 336 | -------------------------------------------------------------------------------- /subghz_ook_to_sub.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # 4 | # Peter Shipley github.com/evilpete 5 | # 6 | # From pkg https://github.com/evilpete/flipper_toolbox 7 | # 8 | # convert .ook files produced by rtl_433 to the Flipper .sub format 9 | 10 | # 11 | # Usage: 12 | # subghz_ook_to_sub.py FILENAME [freq]") 13 | # 14 | # default freq 433920000 [433.92Mhz] 15 | # 16 | # 17 | 18 | 19 | # to convert unsigned 8-bit sdr data do the following: 20 | # 21 | # convert rtl-sdr raw data file into .ook file with rtl_sdr 22 | # (this will partially demodulate the data) 23 | # 24 | # rtl_443 -r rtl_sample.cu8 -w rf_sample.ook 25 | # 26 | # convert the .ook file into a Flipper .sub file 27 | # 28 | # subghz_ook_to_sub.py rf_sample.ook 29 | # 30 | # this will generate the file rf_sample.sub 31 | # 32 | # Note: you may have to manually set the frequancy on the 33 | # command line or by editing the file 34 | 35 | # 36 | # With multiple packets per ook file: 37 | # currently only reads first header and assumes all 38 | # following packets use same modulation 39 | 40 | 41 | # To do: 42 | # parse header 43 | # split samples into multiple files (opton) 44 | # data validation 45 | # .fsk file format ? 46 | # insert breaks between pkts 47 | 48 | 49 | import sys 50 | import os 51 | import time 52 | import pprint 53 | import argparse 54 | 55 | MIN_PULSES = 25 56 | 57 | filen = None 58 | rf_freq = 0 59 | 60 | 61 | rf_freq_default = 433920000 62 | 63 | verbose = 0 64 | 65 | _debug = 0 66 | 67 | # ;pulse data 68 | # ;version 1 69 | # ;timescale 1us 70 | # ;created 2022-11-14 13:59:15-0800 71 | # ;ook 21 pulses 72 | # ;freq1 -75324 73 | # ;centerfreq 0 Hz 74 | # ;samplerate 250000 Hz 75 | # ;sampledepth 8 bits 76 | # ;range 42.1 dB 77 | # ;rssi -0.1 dB 78 | # ;snr 8.0 dB 79 | # ;noise -8.1 dB 80 | # 532 1492 81 | 82 | 83 | def arg_opts(): 84 | 85 | parser = argparse.ArgumentParser(add_help=True, allow_abbrev=True, # noqa 86 | description="Convert rtl_443 .ook format files into .sub format", # noqa 87 | formatter_class=argparse.RawDescriptionHelpFormatter 88 | ) 89 | # argument_default=argparse.SUPPRESS, 90 | 91 | parser.add_argument("-m", "-min", metavar='pulses', dest="min_pulses", 92 | default=None, 93 | help="minimum number of signal pulses") 94 | 95 | parser.add_argument('-v', '--verbose', dest="verb", 96 | default=0, 97 | help='Increase debug verbosity', action='count') 98 | 99 | parser.add_argument("-f", "--freq", metavar='frequency', dest="freq", 100 | default=None, 101 | help="use frequency instead") 102 | 103 | parser.add_argument("-F", "--Freq", metavar='frequency', 104 | dest="default_freq", 105 | default=None, 106 | help=f"default frequency: {rf_freq_default}") 107 | 108 | parser.add_argument("-o", "--out", metavar='output_filename', 109 | dest="outfname", 110 | default=None, 111 | help="output filename") 112 | 113 | # parser.add_argument("-p", "--preface", metavar='preface_duration', 114 | # dest="preface_time", 115 | # default=None, 116 | # type=int, 117 | # help="insert a preface signal (in μs)") 118 | 119 | parser.add_argument("input_file", metavar='input-file', nargs='?', 120 | default=None, 121 | help="file file in .ook format") 122 | 123 | ar, gs = parser.parse_known_args() 124 | 125 | return ar, gs 126 | 127 | 128 | def chunks(lst, n=500): 129 | """Yield successive 500-sized chunks from lst.""" 130 | for i in range(0, len(lst), n): 131 | yield lst[i:i + n] 132 | 133 | 134 | RF_PRESETS = { 135 | 'ook': "FuriHalSubGhzPresetOok650Async", 136 | 'fsk': "FuriHalSubGhzPreset2FSKDev476Async", 137 | } 138 | # Preset: FuriHalSubGhzPreset2FSKDev238Async 139 | # Preset: FuriHalSubGhzPreset2FSKDev476Async 140 | # Preset: FuriHalSubGhzPresetOok270Async 141 | # Preset: FuriHalSubGhzPresetOok650Async 142 | 143 | 144 | def gen_sub(freq, rf_samples): 145 | 146 | if _debug: 147 | print("\n\n\nrf_samples", rf_samples) 148 | 149 | dat = rf_samples[0].get('header') 150 | 151 | if _debug: 152 | print(f"header {dat}") 153 | 154 | comment_text = "generated with https://github.com/evilpete/flipper_toolbox/ook_to_sub.py" 155 | 156 | rf_Preset = None 157 | # Preset: FuriHalSubGhzPreset2FSKDev238Async 158 | # Preset: FuriHalSubGhzPreset2FSKDev476Async 159 | # Preset: FuriHalSubGhzPresetOok270Async 160 | # Preset: FuriHalSubGhzPresetOok650Async 161 | 162 | rf_Preset = RF_PRESETS.get(dat['modulation'], None) 163 | 164 | if rf_Preset is None: 165 | print("Can't determine modulation type from header") 166 | print(dat) 167 | sys.exit(1) 168 | 169 | try: 170 | if rf_freq: 171 | freq = rf_freq 172 | else: 173 | fhz = dat.get('centerfreq', '0 Hz').split()[0] 174 | fhz = int(fhz) 175 | if fhz: 176 | freq = fhz 177 | else: 178 | freq = rf_freq_default 179 | print(f"Using default frequency {rf_freq_default}") 180 | except ValueError: 181 | freq = rf_freq_default 182 | 183 | res = f"""Filetype: Flipper SubGhz RAW File 184 | Version: 1 185 | # {comment_text} 186 | # {time.ctime()} 187 | Frequency: {freq} 188 | Preset: {rf_Preset} 189 | Protocol: RAW 190 | """ 191 | 192 | data = [] 193 | raw_data = [] 194 | 195 | # if args.preface_time: 196 | # raw_data.append(str(args.preface_time * -1)) 197 | 198 | for ds in rf_samples: 199 | data = [] 200 | dat = ds.get('data', []) 201 | 202 | for d in dat: 203 | a = list(map(int, d.split())) 204 | a[1] *= -1 205 | if a[0] == 0: 206 | del a[0] 207 | elif a[1] == 0: 208 | del a[1] 209 | data += a 210 | 211 | data = list(map(str, data)) 212 | 213 | for i in chunks(data): 214 | raw_data.append(f'RAW_Data: {" ".join(i)}') 215 | 216 | res += '\n'.join(raw_data) 217 | 218 | return res 219 | 220 | 221 | def skip_to_next(ffd, symb=";end"): 222 | for line in ffd: 223 | if line.startswith(symb): 224 | return 225 | 226 | 227 | def main(): 228 | 229 | # file_header = {} 230 | 231 | ook_Headers = [";pulse data"] 232 | # samp_mod = "" 233 | # samp_freq1 = 0 234 | # samp_freq2 = 0 235 | 236 | pulse_samples = [] 237 | dat_sample = None 238 | 239 | if _debug: 240 | print(f"open {filen}") 241 | 242 | with open(filen, 'r', encoding="utf-8") as fd: 243 | 244 | header = fd.readline().strip() 245 | if header not in ook_Headers: 246 | print(f"Error: {filen} is not a 'rtl_443 ook' data file") 247 | sys.exit(1) 248 | 249 | for line in fd: 250 | 251 | if line.startswith(';end'): 252 | if _debug: 253 | print("\n\ndat_sample", dat_sample) 254 | print("pulse_samples", pulse_samples) 255 | 256 | if verbose: 257 | print(f"Adding packet with {file_header['pulses']} pulses") 258 | 259 | dat_sample = None 260 | continue 261 | 262 | if dat_sample is None: 263 | dat_sample = {} 264 | dat_sample['header'] = file_header = {} 265 | dat_sample['data'] = pulse_data = [] 266 | pulse_samples.append(dat_sample) 267 | 268 | if line.startswith(';ook') or line.startswith(';fsk'): 269 | a = line[1:].strip().split(None, 2) 270 | if a[1].isnumeric(): 271 | if int(a[1]) < MIN_PULSES: 272 | if verbose: 273 | print(f"skipping packet with {a[1]} pulses") 274 | skip_to_next(fd) 275 | continue 276 | 277 | file_header['pulses'] = int(a[1]) 278 | file_header['modulation'] = a[0] 279 | 280 | if line[0] == ';': 281 | a = line[1:].strip().split(None, 1) 282 | file_header[a[0]] = a[1] 283 | continue 284 | 285 | pulse_data.append(line.strip()) 286 | 287 | print("Total packets in file", len(pulse_samples)) 288 | 289 | sub_data = gen_sub(rf_freq, pulse_samples) 290 | 291 | if _debug or verbose > 2: 292 | # print(f"\n\n{pulse_samples}\n") 293 | pprint.pprint(pulse_samples) 294 | 295 | if args.outfname: 296 | outfilen = args.outfname 297 | if not outfilen.endswith('.sub'): 298 | outfilen += '.sub' 299 | else: 300 | outfilen = os.path.splitext(filen)[0] + ".sub" 301 | 302 | with open(outfilen, 'w', encoding="utf-8") as fd: 303 | print(sub_data, file=fd) 304 | 305 | 306 | if __name__ == '__main__': 307 | 308 | args, _extra = arg_opts() 309 | filen = args.input_file 310 | 311 | if args.freq: 312 | rf_freq = int(args.freq) 313 | 314 | if args.default_freq: 315 | rf_freq_default = int(args.default_freq) 316 | 317 | if args.min_pulses: 318 | MIN_PULSES = int(args.min_pulses) 319 | 320 | if args.verb: 321 | verbose = args.verb 322 | 323 | main() 324 | -------------------------------------------------------------------------------- /subghz_plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | 4 | ir_plot.py 5 | 6 | plot data from flipper subghz raw data save files 7 | 8 | Warning: this is 5 min hack code, use at own risk 9 | 10 | Written By: Peter Shipley github.com/evilpete 11 | 12 | From pkg https://github.com/evilpete/flipper_toolbox 13 | 14 | """ 15 | 16 | 17 | import sys 18 | import os 19 | # from statistics import mean 20 | import argparse 21 | import statistics 22 | # from pprint import pprint 23 | import numpy as np 24 | # import pandas as pd 25 | import matplotlib.pyplot as plt 26 | 27 | verbose = 0 28 | 29 | PRINT_BITS = True # this is a hack 30 | 31 | LOW_PLOT_VAL = 1 32 | HIGH_PLOT_VAL = 5 33 | 34 | MIN_BIT_LEN = 1000 35 | DATA_SCALE = 10 36 | 37 | 38 | def arg_opts(): 39 | 40 | parser = argparse.ArgumentParser(add_help=True, # noqa 41 | formatter_class=argparse.RawDescriptionHelpFormatter) 42 | 43 | parser.add_argument('-v', '--verbose', dest="verbose", 44 | default=0, 45 | help='Increase debug verbosity', action='count') 46 | 47 | parser.add_argument("-s", "--split", dest="split_sig", 48 | action='store_true', 49 | help="try to split and (sub)plot signals separately") 50 | 51 | parser.add_argument("-p", "--preamble", dest="preamble", 52 | type=int, 53 | default=0, 54 | help="split signal break length") 55 | 56 | parser.add_argument("-n", "--numplots", dest="numplots", 57 | type=int, 58 | default=0, 59 | help="maximum number of subplots") 60 | 61 | parser.add_argument("-l", "--length", dest="length", 62 | type=int, 63 | default=0, 64 | help="minimum signal bits") 65 | 66 | parser.add_argument("--seek", dest='seek', 67 | type=int, 68 | default=0, 69 | help="number of samples to skip") 70 | 71 | # parser.add_argument("-m", "--minpulse", dest="minbitlen", 72 | # type=int, 73 | # default=0, 74 | # help="minimum pause length") 75 | 76 | parser.add_argument("-f", "--file", dest="filename", 77 | default=None, 78 | help="Subghz Filename") 79 | 80 | parser.add_argument("-i", "--invert", dest="invert", 81 | default=False, 82 | action='store_true', 83 | help="Invert Wave plot") 84 | 85 | # parser.add_argument("-d", "--dir", dest="destdir", 86 | # default=None, 87 | # help="Destination") 88 | 89 | # parser.add_argument("-o", "--output", dest="out_format", 90 | # choices=['png', 'pdf', 'svg'], 91 | # default="None", 92 | # help="Output Format") 93 | 94 | # parser.add_argument("-s", "--screen", dest="screen", 95 | # default=False, action='store_true', 96 | # help="Display on Screen") 97 | 98 | # data_grp = parser.add_mutually_exclusive_group() 99 | 100 | return parser.parse_known_args() 101 | 102 | 103 | def split_data_str(dat, max_val=8000): 104 | 105 | ret = [] 106 | cur_dat = [] 107 | 108 | for x in dat: 109 | i = abs(x) 110 | if i > max_val: 111 | if cur_dat: 112 | ret.append(cur_dat) 113 | # print(f"cur_dat: {len(cur_dat)}") 114 | cur_dat = [] 115 | else: 116 | cur_dat.append(x) 117 | 118 | ret.append(cur_dat) 119 | 120 | return ret 121 | 122 | 123 | def load_cmd_data(filename): 124 | 125 | ret = [] 126 | with open(filename, 'r', encoding="utf-8") as fd: 127 | 128 | header = fd.readline().strip() 129 | if header != 'Filetype: Flipper SubGhz RAW File': 130 | print(f"Error: {filename} is not a Flipper SubGhz RAW file") 131 | sys.exit(1) 132 | 133 | for line in fd: 134 | 135 | line = line.strip() 136 | 137 | if not line or line[0] == '#': # skip blank lines 138 | continue 139 | 140 | if line.startswith("RAW_Data: "): 141 | a = line[10:].split() 142 | ret.extend(map(int, a)) 143 | 144 | return ret 145 | 146 | 147 | def convert_dat(dat_list, invert=False, divider=0): # normalize=0, 148 | 149 | high_val = HIGH_PLOT_VAL 150 | low_val = LOW_PLOT_VAL 151 | 152 | if len(dat_list) % 2 != 0: 153 | dat_list.append(0) 154 | 155 | dat_len = len(dat_list) 156 | 157 | if verbose > 1: 158 | print(f"dat_len {dat_len}") 159 | 160 | if invert: 161 | high_val = LOW_PLOT_VAL 162 | low_val = HIGH_PLOT_VAL 163 | 164 | res = [low_val] 165 | for i in dat_list: 166 | 167 | if divider: 168 | i //= divider 169 | 170 | if i > 0: 171 | res += [high_val] * i 172 | else: 173 | res += [low_val] * abs(i) 174 | 175 | res.append(1) 176 | 177 | # print("\n") 178 | return res 179 | 180 | 181 | def main(arg, av): 182 | 183 | # disp = False 184 | 185 | # get input filename from argparse or fist arg 186 | if arg.filename: 187 | fname = arg.filename 188 | elif av: 189 | fname = av.pop(0) 190 | 191 | raw_dat = load_cmd_data(fname) 192 | 193 | if arg.seek: 194 | raw_dat = raw_dat[:arg.seek] 195 | 196 | # max_sig = max(raw_dat) 197 | # print(f"min {min_sig}") 198 | # print(f"max {max_sig}") 199 | # max_pause = int(statistics.mean(a_dat)) 200 | 201 | a_dat = list(map(abs, raw_dat)) 202 | 203 | # print(f"max_pause max//2 {max_pause}") 204 | # max_pause = int(statistics.mean(a_dat)) * 3 205 | max_pause = int(statistics.stdev(a_dat)) * 2 206 | 207 | max_pause = (arg.preamble or max_pause) 208 | # max(min_sig, max_sig) // 2) 209 | 210 | if verbose: 211 | print(f"max_pause {max_pause}") 212 | 213 | if arg.preamble or arg.split_sig: 214 | # print(f"using max_val {max_pause}") 215 | dat_list = split_data_str(raw_dat, max_val=max_pause) 216 | else: 217 | dat_list = [raw_dat] 218 | 219 | if verbose: 220 | print(f"dat_list {len(dat_list)}") 221 | 222 | min_length = (arg.length or MIN_BIT_LEN) 223 | plot_list = [] 224 | for x in dat_list: 225 | y = convert_dat(x, divider=DATA_SCALE) 226 | if len(y) >= min_length: 227 | plot_list.append(convert_dat(x, divider=10)) 228 | 229 | # print(f"plot_list {len(plot_list)}") 230 | 231 | # plot_list = plot_list[:8] 232 | 233 | if arg.numplots: 234 | plot_list = plot_list[:arg.numplots] 235 | 236 | list_lenghts = [len(x) for x in plot_list] 237 | 238 | max_len = max(list_lenghts) 239 | 240 | if verbose: 241 | print(f"max_len {max_len}") 242 | 243 | plot_x = np.arange(max_len*DATA_SCALE, step=DATA_SCALE) 244 | 245 | plt.style.use("dark_background") 246 | p = plt.figure() # facecolor='yellow') 247 | ax = p.gca() 248 | ax.get_yaxis().set_visible(False) 249 | plt.xlabel('μs') 250 | 251 | plt.title("SubGhz Raw Signal") 252 | 253 | height = 6 254 | pn = len(plot_list) 255 | if pn < 8: 256 | height = 2 + pn * .5 257 | plt.gcf().set_size_inches(6, height) 258 | # plt.figure(facecolor='yellow') 259 | 260 | y_off = 0 261 | for d in plot_list: 262 | d_len = len(d) 263 | # if d_len < max_len: 264 | # ln = max_len - d_len 265 | # d += [1] * ln 266 | 267 | plot_y = np.array(d) + (y_off * int(HIGH_PLOT_VAL * 1.3)) 268 | 269 | plt.plot(plot_x[:d_len], plot_y) 270 | 271 | y_off += 1 272 | 273 | # if arg.out_format == 'png': 274 | # if arg.verbose: 275 | # print(f'{destdir}/{ii}_{cmd_name}.png y_off={y_off}') 276 | outfile = os.path.basename(fname) 277 | outfile = os.path.splitext(outfile)[0] + ".png" 278 | print(f"saving plot as {outfile}") 279 | 280 | plt.savefig(outfile, pad_inches=0.3) 281 | 282 | # if disp: 283 | plt.show() 284 | 285 | 286 | if __name__ == '__main__': 287 | ag, agv = arg_opts() 288 | # print("arg", arg, "av=", av) 289 | 290 | if ag.verbose: 291 | verbose = ag.verbose 292 | 293 | main(ag, agv) 294 | -------------------------------------------------------------------------------- /subghz_preset_gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | 4 | subghz_preset_gen.py 5 | 6 | Modify / Generates CC1101 "SubGhzPresetCustom" settings 7 | 8 | Written By: Peter Shipley github.com/evilpete 9 | 10 | From pkg https://github.com/evilpete/flipper_toolbox 11 | 12 | """ 13 | 14 | # import sys 15 | # import os 16 | # import pprint 17 | import argparse 18 | 19 | from subghz_decode_presets import CC_Config # CC_REG 20 | 21 | # ppylint: disable=no-member 22 | 23 | _DEBUG = 0 24 | 25 | 26 | # See : https://github.com/flipperdevices/flipperzero-firmware/blob/dev/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h 27 | 28 | 29 | rf_presets = { 30 | # PresetOok270Async : OOK, bandwidth 270kHz, async 31 | "AM270": ("Custom_preset_data: 02 0D 03 47 08 32 0B 06 10 67 11 32 " 32 | "12 30 13 00 14 00 18 18 19 18 1B 03 1C 00 1D 40 20 FB " 33 | "21 B6 22 11 00 00 00 C0 00 00 00 00 00 00"), 34 | 35 | # PresetOok650Async : OOK, bandwidth 650kHz, async 36 | "AM650": ("Custom_preset_data: 02 0D 03 07 08 32 0B 06 10 17 11 32 " 37 | "12 30 13 00 14 00 18 18 19 18 1B 07 1C 00 1D 91 20 FB " 38 | "21 B6 22 11 00 00 00 C0 00 00 00 00 00 00"), 39 | 40 | # Preset2FSKDev238Async : FM, deviation 2.380371 kHz, async 41 | "FM238": ("Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 67 11 83 " 42 | "12 04 13 02 14 00 15 04 18 18 19 16 1B 07 1C 00 1D 91 20 FB " 43 | "21 56 22 10 00 00 C0 00 00 00 00 00 00 00"), 44 | 45 | # Preset2FSKDev476Async : FM, deviation 47.60742 kHz, async 46 | "FM476": ("Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 67 11 83 " 47 | "12 04 13 02 14 00 15 47 18 18 19 16 1B 07 1C 00 1D 91 20 FB " 48 | "21 56 22 10 00 00 C0 00 00 00 00 00 00 00"), 49 | 50 | # PresetMSK99_97KbAsync : MSK, deviation 47.60742 kHz, 99.97Kb/s, async 51 | "MSK999": ("Custom_preset_data: 02 06 03 07 04 46 05 4c 06 00 08 05 " 52 | "09 00 0a 00 0b 06 0c 23 10 5b 11 f8 12 72 13 22 14 f8 15 " 53 | "47 18 18 19 16 1a 1c 1b c7 1c 00 1d b2 21 56 22 10 29 59 " 54 | "00 00 c0 00 00 00 00 00 00 00"), 55 | 56 | # PresetGFSK9_99KbAsync : GFSK, deviation 19.042969 kHz, 9.996Kb/s, async 57 | "GFSK99": ("Custom_preset_data: 02 06 03 47 04 46 05 4c 06 00 08 05 " 58 | "09 00 0b 06 10 c8 11 93 12 12 15 34 18 18 19 16 1b 43 " 59 | "1c 40 1d 91 20 fb 00 00 c0 00 00 00 00 00 00 00"), 60 | } 61 | 62 | 63 | MOD_2FSK = 0x00 64 | MOD_GFSK = 0x10 65 | MOD_ASK_OOK = 0x30 66 | MOD_4FSK = 0x40 67 | MOD_MSK = 0x70 68 | 69 | PKT_FMT = { 70 | "Normal": 0x00, 71 | "Sync": 0x01, 72 | "Random": 0x02, 73 | "Async": 0x03, 74 | } 75 | 76 | mods = { 77 | "2FSK": 0x00, # MOD_2FSK, 78 | "GFSK": 0x10, # MOD_GFSK, 79 | "OOK": 0x30, # MOD_ASK_OOK 80 | "4FSK": 0x40, # MOD_4FSK 81 | "MSK": 0x70, # MOD_MSK 82 | } 83 | 84 | sync_modes = ['SYNCM_NONE', 'SYNCM_15_of_16', 'SYNCM_16_of_16', 85 | 'SYNCM_30_of_32', 'SYNCM_CARRIER', 'SYNCM_CARRIER_15_of_16', 86 | 'SYNCM_CARRIER_16_of_16', 'SYNCM_CARRIER_30_of_32'] 87 | 88 | sync_help = """ 89 | SYNCM_NONE = 0 90 | SYNCM_15_of_16 = 1 91 | SYNCM_16_of_16 = 2 92 | SYNCM_30_of_32 = 3 93 | SYNCM_CARRIER = 4 94 | SYNCM_CARRIER_15_of_16 = 5 95 | SYNCM_CARRIER_16_of_16 = 6 96 | SYNCM_CARRIER_30_of_32 = 7 97 | """ 98 | 99 | 100 | def _interpret_val(opt): 101 | opt = opt.upper() 102 | 103 | if opt in ["ON", "TRUE", "T", "YES", "Y", "1"]: 104 | return 1 105 | 106 | if opt in ["OFF", "FALSE", "F", "NO", "N", "0"]: 107 | return 0 108 | 109 | if opt.isdigit(): 110 | return int(opt) 111 | 112 | return None 113 | 114 | 115 | def arg_opts(): 116 | """argument parse""" 117 | 118 | preset_namelist = sorted(rf_presets.keys()) 119 | modulation_namelist = sorted(CC_Config.mod_num.values()) 120 | length_namelist = sorted(CC_Config.PKT_LENGTH_CONF.keys()) 121 | pkt_fmt_namelist = sorted(PKT_FMT.keys()) 122 | # modulation_namelist = sorted(mods.keys()) 123 | # length_namelist = sorted(length_conf.keys()) 124 | 125 | parser = argparse.ArgumentParser(add_help=True, allow_abbrev=True, 126 | formatter_class=argparse.RawDescriptionHelpFormatter) 127 | # argument_default=argparse.SUPPRESS, 128 | 129 | parser.add_argument("-p", "--preset", dest="preset_profile", 130 | choices=preset_namelist, 131 | default=None, 132 | help="preset profile") 133 | 134 | parser.add_argument("-pr", "--print", dest="print_profile", 135 | action='store_true', 136 | default=None, 137 | help="Print Profile Description") 138 | 139 | parser.add_argument("-sw", "--syncword", dest="sync_word", 140 | type=int, 141 | # choices=sync_modes, 142 | default=None, 143 | help="Sync Word") 144 | 145 | parser.add_argument("-sm", "--syncmode", dest="sync_mode", 146 | type=int, 147 | # choices=sync_modes, 148 | default=None, 149 | help=sync_help) 150 | 151 | parser.add_argument("-mod", "--modulation", dest="modulation", 152 | choices=modulation_namelist, 153 | default=None, 154 | help="Modulation") 155 | 156 | parser.add_argument("-lc", "--length_conf", dest="length_conf", 157 | choices=length_namelist, 158 | default=None, 159 | help="Length Config") 160 | 161 | parser.add_argument("-pf", "--pktfmt", dest="pkt_fmt", 162 | choices=pkt_fmt_namelist, 163 | default="Async", 164 | help="Packet Format") 165 | 166 | parser.add_argument("-pl", "--pkt_len", dest="pkt_len", 167 | type=int, 168 | default=None, 169 | help="Packet Length") 170 | 171 | parser.add_argument('-v', '--verbose', dest="verbose", 172 | default=0, 173 | help='Increase debug verbosity', action='count') 174 | 175 | parser.add_argument("-n", "--name", dest="conf_name", 176 | default="NewPreset", 177 | help="Name For Preset") 178 | 179 | parser.add_argument("-if", "--IntermediateFreq", dest="intermediate_freq", 180 | type=int, 181 | default=None, 182 | help="Intermediate frequency") 183 | 184 | parser.add_argument("-dr", "--datarate", dest="data_rate", 185 | type=int, 186 | default=None, 187 | help="Date Rate") 188 | 189 | parser.add_argument("-fr", "--frequency", dest="frequency", 190 | type=int, 191 | default=None, 192 | help="frequency") 193 | 194 | parser.add_argument("-bw", "--bandwidth", dest="band_width", 195 | type=int, 196 | default=None, 197 | help="Band Width") 198 | 199 | parser.add_argument("-np", "--numpreamble", dest="num_preamble", 200 | type=int, 201 | default=None, 202 | help="Minimum number of preamble bytes to be transmitted\n" 203 | "0=2bytes 1=3b 2=4b 3=6b 4=8b 5=12b 6=15b 7=24b" 204 | ) 205 | 206 | parser.add_argument("-dev", "--deviation", dest="deviation", 207 | type=int, 208 | default=None, 209 | help="FM Deviation") 210 | 211 | parser.add_argument("-cs", "--channelspacing", "--spacing", dest="channel_spacing", 212 | type=int, 213 | default=None, 214 | help="Channel Spacing") 215 | 216 | parser.add_argument("-man", "--manchester", dest="manchester", 217 | default=False, action='store_true', 218 | help="Manchester Encoding") 219 | 220 | # crc_grp = parser.add_mutually_exclusive_group() 221 | 222 | parser.add_argument("-crc", "--enable_crc", dest="enable_crc", 223 | choices=['on', 'off'], 224 | default=None, 225 | help="Enable/Disable CRC") 226 | 227 | parser.add_argument("-dw", "--datawhitening", "--datawhite", dest="data_whiten", 228 | choices=['on', 'off'], 229 | default=None, 230 | help="Enable/DisableData Whitening") 231 | 232 | 233 | # data_grp.add_argument("-c", "--cmd-file", dest="cmd_file", 234 | # type=argparse.FileType('r', encoding='UTF-8'), 235 | # default=None, 236 | # help="Command File") 237 | 238 | return parser.parse_known_args() 239 | 240 | 241 | def main(): 242 | 243 | # print(rf_presets) 244 | 245 | reg_conf = CC_Config() 246 | reg_conf.reg_list[2] = 13 # Output Pin Configuration 247 | reg_conf.reg_list[3] = 7 # RX FIFO and TX FIFO Thresholds 248 | 249 | 250 | args, u = arg_opts() 251 | 252 | # print(f"args: {args}\n") 253 | # print(f"u: {u}\n") 254 | 255 | if args.preset_profile: 256 | reg_conf.load_str(rf_presets[args.preset_profile]) 257 | 258 | if args.deviation is not None: 259 | reg_conf.set_Deviatn(args.deviation) 260 | 261 | if args.modulation is not None: 262 | reg_conf.set_Modulation(args.modulation) 263 | 264 | if args.manchester is not None: 265 | reg_conf.set_Manchester(args.manchester) 266 | 267 | if args.length_conf is not None: 268 | reg_conf.set_Pktlen_conf(args.length_conf) 269 | 270 | if args.pkt_len is not None: 271 | if args.pkt_len > 255: 272 | raise ValueError("Max Packet Length 255") 273 | reg_conf.set_pktlen(args.pkt_len) 274 | 275 | if args.intermediate_freq is not None: 276 | reg_conf.set_FsIF(args.intermediate_freq) 277 | 278 | if args.data_rate is not None: 279 | reg_conf.set_DRate(args.data_rate) 280 | 281 | if args.channel_spacing is not None: 282 | reg_conf.set_ChanSpc(args.channel_spacing) 283 | 284 | if args.enable_crc is not None: 285 | if args.enable_crc == 'on': 286 | reg_conf.set_Enable_CRC(enable=True) 287 | else: 288 | reg_conf.set_Enable_CRC(enable=False) 289 | 290 | if args.data_whiten is not None: 291 | if args.data_whiten == 'yes': 292 | reg_conf.set_PktDataWhitening(1) 293 | else: 294 | reg_conf.set_PktDataWhitening(0) 295 | 296 | if args.num_preamble is not None: 297 | reg_conf.set_NumPreamble(args.num_preamble) 298 | 299 | if args.band_width is not None: 300 | if args.band_width < 54170: 301 | raise ValueError("Bandwith must me over 54kHz") 302 | reg_conf.set_ChanBW(args.band_width) 303 | 304 | if args.frequency is not None: 305 | reg_conf.set_Freq(args.frequency) 306 | print("Warning: frequency is set in subghz flipper file") 307 | 308 | if args.pkt_fmt is not None: 309 | if args.pkt_fmt in PKT_FMT: 310 | reg_conf.set_pktfmt(args.pkt_fmt) 311 | 312 | mod = reg_conf.get_Modulation() 313 | manch = reg_conf.get_Manchester() 314 | 315 | if mod == 0x30 and args.deviation: 316 | print("Warning: Deviation value is ignored when modulation is 'OOK'") 317 | 318 | if mod is not None and manch is not None: 319 | if args.modulation == 0x40 and manch: 320 | print("Warning: radio doesn't support Manchester encoding in 4FSK") 321 | 322 | # print("as_preset_tuples:\n", pprint.pformat(reg_conf.as_preset_data_tuples(), compact=True)) 323 | 324 | print("\n") 325 | print(f"Custom_preset_name: {args.conf_name}\n" 326 | "Custom_preset_module: CC1101\n" 327 | "Custom_preset_data:", reg_conf.as_preset_data()) 328 | print("\n") 329 | 330 | if args.print_profile: 331 | for a, b in reg_conf.rf_conf(): 332 | print(f" {a:<28s} {b:<10s}") 333 | 334 | # print(reg_conf.as_tuples()) 335 | 336 | 337 | if __name__ == '__main__': 338 | main() 339 | -------------------------------------------------------------------------------- /subghz_secplusv1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Display and/or edit Flipper SubGhz Security+ 1.0 Key Files 4 | 5 | Peter Shipley github.com/evilpete 6 | 7 | From pkg https://github.com/evilpete/flipper_toolbox 8 | """ 9 | 10 | # **WORK IN PROGRESS** 11 | 12 | import sys 13 | import time 14 | # import os 15 | # from typing import Iterable, Union, Any 16 | import random 17 | import argparse 18 | 19 | _debug = 0 20 | 21 | MAX_FIXED = 3**20 - 1 22 | MAX_ID = 3**17 - 1 23 | TX_FREQ = 315000000 24 | 25 | BUTTON_NAMES = ["Middle", "Left", "Right"] 26 | 27 | 28 | # https://stackoverflow.com/questions/2267362/how-to-convert-an-integer-to-a-string-in-any-base 29 | def numToBase(n, b): 30 | if n == 0: 31 | return [0] 32 | digits = [] 33 | while n: 34 | digits.append(int(n % b)) 35 | n //= b 36 | return digits[::-1] 37 | 38 | 39 | def numToBase_str(n, b): 40 | a = numToBase(n, b) 41 | rstr = map(str, a) 42 | return "".join(rstr) 43 | 44 | 45 | def arg_opts(): 46 | 47 | parser = argparse.ArgumentParser(add_help=True, allow_abbrev=True, # noqa 48 | description="display and/or edit Flipper SubGhz Security+ 1.0 Key Files", 49 | formatter_class=argparse.RawDescriptionHelpFormatter 50 | ) 51 | # argument_default=argparse.SUPPRESS, 52 | 53 | parser.add_argument("-r", "--rolling", metavar='rolling_code', dest="rolling", 54 | default=None, 55 | help="Rolling Count") 56 | 57 | parser.add_argument("-b", "--button", metavar='button_id', dest="button", 58 | # type=int, 59 | default=None, 60 | help="Button: 0=Middle 1=Left 2=Right") 61 | 62 | fixed_grp = parser.add_mutually_exclusive_group() 63 | 64 | fixed_grp.add_argument("-f", "--fixed", metavar='fixed_code', dest="fixed", 65 | default=0, 66 | help="fixed code value") 67 | 68 | fixed_grp.add_argument("-i", "--id", metavar='remote_id', dest="id", 69 | default=None, 70 | help="Remote-ID") 71 | 72 | parser.add_argument("-q", "--quiet", dest="quiet", 73 | default=None, 74 | action='store_true', 75 | help="run quietly") 76 | 77 | parser.add_argument("-o", "--out", metavar='output_filename', dest="outfname", 78 | default=None, 79 | help="output filename, use '-' for stdout") 80 | 81 | parser.add_argument("input_file", metavar='input-file', nargs='?', 82 | # "-F", "--File", dest="input_file", 83 | # type=argparse.FileType('r', encoding='UTF-8'), 84 | default=None, 85 | help="Flipper Subghz File") 86 | 87 | # parser.add_argument("-h", "--freq", "--hz", dest="send_freq", 88 | # type=int, 89 | # default=315000000, 90 | # help="Transmit frequency") 91 | 92 | ar, gs = parser.parse_known_args() 93 | 94 | ar.rolling = conv_int(ar.rolling) 95 | ar.fixed = conv_int(ar.fixed) 96 | ar.id = conv_int(ar.id) 97 | 98 | if ar.rolling and int(ar.rolling) >= 2**32: 99 | raise ValueError("Rolling code must be less than 2^32") 100 | 101 | if ar.fixed and int(ar.fixed) >= MAX_FIXED: 102 | raise ValueError(f"Fixed code must be less than 3^20 ({MAX_FIXED})") 103 | 104 | if ar.id and int(ar.id) > MAX_ID: 105 | raise ValueError(f"Remote ID must be less than 3^17 ({MAX_ID})") 106 | 107 | # ar.button.isdigit() and int(ar.button) <= 2: 108 | if ar.button and ar.button not in ['0', '1', '2']: 109 | raise ValueError(f"Button value must be between 0 -> 2 ({ar.button})") 110 | 111 | return ar, gs 112 | 113 | 114 | SUBGHZ_KEY_FILE_TYPE = "Flipper SubGhz Key File" 115 | 116 | 117 | def read_file(fd): 118 | 119 | key_dat = None 120 | header = fd.readline().strip() 121 | 122 | a = header.split(':', 1) 123 | if not (a[0].startswith("Filetype") 124 | and a[1].strip() == SUBGHZ_KEY_FILE_TYPE): 125 | print("invalid filetype") 126 | sys.exit(0) 127 | 128 | for line in fd: 129 | a = line.split(':', 1) 130 | 131 | if a[0].startswith("Protocol"): 132 | if a[1].strip() != "Security+ 1.0": 133 | sys.exit(0) 134 | 135 | if a[0].startswith("Key"): 136 | key_dat = a[1].strip().split() 137 | 138 | if _debug: 139 | print("read_file", key_dat) 140 | 141 | if key_dat: 142 | # return "".join(key_dat), "".join(pkt_dat) 143 | return key_dat 144 | 145 | return None 146 | 147 | 148 | def write_file(rol, fix, fname=None, quiet=False): 149 | 150 | hf = f"{fix:08X}" 151 | hr = f"{rol:08X}" 152 | # print(f"1: {ha}") 153 | # print(f"2: {hb}") 154 | hval = hf + hr 155 | 156 | key_str = " ".join([hval[i:i + 2] for i in range(0, 16, 2)]) 157 | 158 | comment_str = pretty_print(rol, fix) 159 | 160 | # Id:{fix // 27:08X} ({fix // 27}) Rolling:{rol:02X} ({rol}) 161 | ret = f"""Filetype: Flipper SubGhz Key File 162 | Version: 1 163 | # Generated with https://github.com/evilpete/flipper_toolbox 164 | # {time.ctime()} 165 | # {comment_str} 166 | Frequency: {TX_FREQ} 167 | Preset: FuriHalSubGhzPresetOok650Async 168 | Protocol: Security+ 1.0 169 | Bit: 42 170 | Key: {key_str} 171 | """ # noqa 172 | 173 | if _debug: 174 | print(ret) 175 | print(ret) 176 | 177 | if fname is None: 178 | fname = f"secv1-{fix:010X}.sub" 179 | 180 | if fname == '-': 181 | sys.stdout.write(ret) 182 | else: 183 | if not fname.endswith('.sub'): 184 | fname += '.sub' 185 | 186 | if not quiet: 187 | print(f"writting: {fname}") 188 | 189 | with open(fname, "w", encoding="utf-8") as fd: 190 | fd.write(ret) 191 | 192 | 193 | # SUBGHZ_KEY_FILE_TYPE "Flipper SubGhz Key File" 194 | 195 | hex_set = set('abcdefABCDEF0123456789') 196 | 197 | 198 | def is_hex_str(s): 199 | return set(s).issubset(hex_set) 200 | 201 | 202 | def conv_int(arg): 203 | 204 | if arg == 0: 205 | return 0 206 | 207 | if not arg: 208 | return None 209 | 210 | if arg[:2].lower() in ['0b', '0x']: 211 | return int(arg, 0) 212 | 213 | if arg.isdigit(): 214 | return int(arg) 215 | 216 | if is_hex_str(arg): 217 | return int(arg, 16) 218 | 219 | return None 220 | 221 | 222 | def pretty_print(rolling, fixed): 223 | 224 | ret_str = f"Rolling={rolling}," 225 | ret_str += f" Fixed={fixed}," 226 | 227 | fixed_b3 = numToBase_str(fixed, 3) 228 | 229 | b_id = fixed_b3[-1] # a_base3.pop(0) 230 | id0 = fixed_b3[-2] 231 | id1 = fixed_b3[-3] 232 | 233 | if id1 == "1": 234 | remote_id3 = fixed_b3[:-3] 235 | remote_id = int(remote_id3, 3) 236 | 237 | button_id = BUTTON_NAMES[int(b_id)] 238 | 239 | ret_str += f" id0={id0}, id1={id1}," 240 | ret_str += f" Remote_id={remote_id} ({remote_id:08X})," 241 | ret_str += f" Button_id={button_id} ({b_id})" 242 | 243 | elif id1 == "0": 244 | pad_id3 = fixed_b3[-10:-3] 245 | pad_id = int(pad_id3, 3) 246 | pin3 = fixed_b3[-19:-10] 247 | pin = int(pin3, 3) 248 | 249 | ret_str += f" pad_id={pad_id}" 250 | if pin <= 9999: 251 | ret_str += f" pin={0:04}" 252 | elif pin <= 11029: 253 | ret_str += f" pin=Enter ({pin})" 254 | 255 | pin_suffix = fixed_b3[0] 256 | if pin_suffix == "1": 257 | ret_str += " #" 258 | elif pin_suffix == "2": 259 | ret_str += " *" 260 | 261 | return ret_str 262 | 263 | 264 | def main(): 265 | 266 | rolling_dat = fixed_dat = 0 267 | 268 | args, _extra = arg_opts() 269 | 270 | if _debug: 271 | print("args", args) 272 | print("_extra", _extra) 273 | 274 | if args.input_file: 275 | with open(args.input_file, 'r', encoding='UTF-8') as fd: 276 | xx = read_file(fd) 277 | 278 | if xx: 279 | s_key = "".join(xx) 280 | i_key = int(s_key, 16) 281 | b_key = f"{i_key:08b}" 282 | 283 | if _debug: 284 | print(s_key) 285 | print(i_key) 286 | print(b_key[6:]) 287 | 288 | fixed_dat = (i_key >> 32) & 0xFFFFFFFF 289 | 290 | rolling_dat = i_key & 0xFFFFFFFF 291 | 292 | if _debug: # and (fixed_out or rolling_out): 293 | print(f">> fixed_dat {fixed_dat:12d} " 294 | f"{fixed_dat:010X} {fixed_dat:016b} " 295 | + numToBase_str(fixed_dat, 3)) 296 | print(f">> rolling_dat {rolling_dat:12d} " 297 | f"{rolling_dat:010X} {rolling_dat:040b} " 298 | + numToBase_str(rolling_dat, 3)) 299 | 300 | a_fixed = args.fixed or fixed_dat 301 | 302 | a_id0 = a_id1 = a_remote_id = a_but = None 303 | 304 | if a_fixed: 305 | a_base3 = numToBase_str(a_fixed, 3) 306 | a_but = a_base3[-1] # a_base3.pop(0) 307 | a_id0 = a_base3[-2] 308 | a_id1 = a_base3[-3] 309 | a_remote_id3 = a_base3[:-3] 310 | a_remote_id = int(a_remote_id3, 3) 311 | 312 | r_but = args.button or a_but or 1 313 | 314 | # 129140162 315 | r_id = args.id or a_remote_id or random.randint(2**22, MAX_ID - 1) 316 | 317 | r_rolling = (args.rolling or rolling_dat or 1) & 0xFFFFFFFF # 32 bits max 318 | 319 | # fixed_code = (r_id & 0xffffffff) | (r_button << 32) 320 | 321 | # button_code = random.randint(3, 172) | 0x01 322 | 323 | if _debug: 324 | print(f"r_button {r_but}") 325 | print(f"r_id {r_id:12d} {r_id:010X} {r_id:016b}", numToBase_str(r_id, 3)) 326 | print(f"r_rolling {r_rolling:12d} {r_rolling:010X} {r_rolling:016b}", numToBase_str(r_rolling, 3)) 327 | # print(f"fixed_code {fixed_code:12d} {fixed_code:010X} {fixed_code:040b}") # noqa 328 | 329 | r_id3 = numToBase_str(r_id, 3) 330 | r_id0 = a_id0 or "0" 331 | r_id1 = a_id1 or "1" 332 | 333 | r_fixed3 = r_id3 + r_id1 + r_id0 + str(r_but) 334 | 335 | r_fixed = int(r_fixed3, 3) 336 | # print(f"r_fixed {r_fixed:12d} {r_rolling:010X} {r_fixed:016b} {r_fixed3}") 337 | 338 | if not args.quiet: 339 | pretty_out = pretty_print(r_rolling, r_fixed) 340 | print(f"\nSecurity+ V1: {pretty_out}\n") 341 | 342 | # only save to file if new of changed 343 | if (args.fixed or args.button or args.id or args.rolling) or not fixed_dat: 344 | write_file(r_rolling, r_fixed, args.outfname, args.quiet) 345 | 346 | 347 | if __name__ == '__main__': 348 | main() 349 | -------------------------------------------------------------------------------- /subghz_secplusv2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Display and/or edit Flipper SubGhz Security+ 2.0 Key Files 5 | 6 | Peter Shipley github.com/evilpete 7 | 8 | From pkg https://github.com/evilpete/flipper_toolbox 9 | """ 10 | 11 | # **WORK IN PROGRESS** 12 | 13 | import sys 14 | import os 15 | import time 16 | 17 | # from typing import Iterable, Union, Any 18 | import random 19 | import json 20 | import argparse 21 | import pprint 22 | 23 | try: 24 | import secplus.secplus as Secplus 25 | except ImportError as e: 26 | print("Failed to import secplus") 27 | print("https://github.com/argilo/secplus/blob/master/secplus.py") 28 | sys.exit(0) 29 | 30 | _debug = 0 31 | 32 | TX_FREQ = 315000000 33 | 34 | 35 | def arg_opts(): 36 | parser = argparse.ArgumentParser( 37 | add_help=True, 38 | allow_abbrev=True, # noqa 39 | description="display and/or edit Flipper SubGhz Security+ 2.0 Key Files", # noqa 40 | formatter_class=argparse.RawDescriptionHelpFormatter, 41 | ) 42 | # argument_default=argparse.SUPPRESS, 43 | 44 | parser.add_argument( 45 | "-r", 46 | "-rolling", 47 | metavar="rolling_code", 48 | dest="rolling", 49 | default=None, 50 | help="Rolling Count", 51 | ) 52 | 53 | parser.add_argument( 54 | "-b", "--button", metavar="button_id", dest="button", default=0, help="Button" 55 | ) 56 | 57 | fixed_grp = parser.add_mutually_exclusive_group() 58 | 59 | fixed_grp.add_argument( 60 | "-f", 61 | "--fixed", 62 | metavar="fixed_code", 63 | dest="fixed", 64 | default=0, 65 | help="fixed code value", 66 | ) 67 | 68 | fixed_grp.add_argument( 69 | "-i", "--id", metavar="remote_id", dest="id", default=None, help="Remote-ID" 70 | ) 71 | 72 | parser.add_argument( 73 | "-q", 74 | "--quiet", 75 | dest="quiet", 76 | default=None, 77 | action="store_true", 78 | help="run quietly", 79 | ) 80 | 81 | parser.add_argument( 82 | "-J", 83 | "--Json", 84 | metavar="rtl_log.json", 85 | dest="json_log", 86 | default=None, 87 | help="Read rtl_433 json log", 88 | ) 89 | 90 | parser.add_argument( 91 | "-o", 92 | "--out", 93 | metavar="output_filename", 94 | dest="outfname", 95 | default=None, 96 | help="output filename, use '-' for stdout", 97 | ) 98 | 99 | parser.add_argument( 100 | "input_file", 101 | metavar="input-file", 102 | nargs="?", 103 | default=None, 104 | help="Flipper Subghz File", 105 | ) 106 | 107 | # parser.add_argument("-h", "--freq", "--hz", dest="send_freq", 108 | # type=int, 109 | # default=315000000, 110 | # help="Transmit frequency") 111 | 112 | ar, gs = parser.parse_known_args() 113 | 114 | ar.rolling = conv_int(ar.rolling) 115 | ar.id = conv_int(ar.id) 116 | ar.fixed = conv_int(ar.fixed) 117 | ar.button = conv_int(ar.button) 118 | 119 | return ar, gs 120 | 121 | 122 | # Filetype: Flipper SubGhz Key File 123 | # Version: 1 124 | # Frequency: 315000000 125 | # Preset: FuriHalSubGhzPresetOok650Async 126 | # Protocol: Security+ 2.0 127 | # Bit: 62 128 | # Key: 00 00 3D 10 02 09 FA F6 129 | # Secplus_packet_1: 00 00 3C 29 37 7F 38 F3 130 | 131 | SUBGHZ_KEY_FILE_TYPE = "Flipper SubGhz Key File" 132 | 133 | 134 | def read_file(fd): 135 | key_dat = pkt_dat = None 136 | header = fd.readline().strip() 137 | 138 | a = header.split(":", 1) 139 | if not (a[0].startswith("Filetype") and a[1].strip() == SUBGHZ_KEY_FILE_TYPE): 140 | print("invalid filetype") 141 | sys.exit(0) 142 | 143 | for line in fd: 144 | a = line.split(":", 1) 145 | 146 | if a[0].startswith("Protocol"): 147 | if a[1].strip() != "Security+ 2.0": 148 | print("invalid Protocol") 149 | sys.exit(0) 150 | 151 | if a[0].startswith("Key"): 152 | key_dat = a[1].strip().split() 153 | 154 | if a[0].startswith("Secplus_packet_1"): 155 | pkt_dat = a[1].strip().split() 156 | # replace(" ", "") 157 | 158 | if _debug: 159 | print("read_file", key_dat, pkt_dat) 160 | 161 | if key_dat and pkt_dat: 162 | return "".join(key_dat), "".join(pkt_dat) 163 | # return key_dat, pkt_dat 164 | 165 | return None, None 166 | 167 | 168 | def print_file(rol, fix, fname=None, quiet=False, rf_freq=None): 169 | seq_v2 = Secplus.encode_v2(rol, fix) 170 | seq_v2_str = "".join(map(str, seq_v2)) 171 | if _debug: 172 | print(seq_v2_str[:40], "---", seq_v2_str[40:]) 173 | 174 | ia = int("00000000001111" + "00" + seq_v2_str[:40], 2) 175 | ib = int("00000000001111" + "01" + seq_v2_str[40:], 2) 176 | 177 | ha = f"{ia:016X}" 178 | hb = f"{ib:016X}" 179 | # print(f"1: {ha}") 180 | # print(f"2: {hb}") 181 | 182 | p1_str = " ".join([ha[i: i + 2] for i in range(0, 16, 2)]) 183 | p2_str = " ".join([hb[i: i + 2] for i in range(0, 16, 2)]) 184 | 185 | tx_freq = rf_freq or TX_FREQ 186 | 187 | ret = f"""Filetype: Flipper SubGhz Key File 188 | Version: 1 189 | # Button:{fix>>32:02X} ({fix>>32}) Id:{fix&0xffffffff:08X} ({fix&0xffffffff}) Rolling:{rol:02X} ({rol}) 190 | # Generated with https://github.com/evilpete/flipper_toolbox 191 | # {time.ctime()} 192 | Frequency: {tx_freq} 193 | Preset: FuriHalSubGhzPresetOok650Async 194 | Protocol: Security+ 2.0 195 | Bit: 62 196 | Key: {p2_str} 197 | Secplus_packet_1: {p1_str} 198 | """ # noqa 199 | 200 | if _debug: 201 | print(ret) 202 | 203 | if fname is None: 204 | fname = f"secv2-{fix:010X}.sub" 205 | elif os.path.isdir(fname): 206 | fname = f"{fname}/secv2-{fix:010X}.sub" 207 | 208 | if fname == "-": 209 | sys.stdout.write(ret) 210 | else: 211 | if not fname.endswith(".sub"): 212 | fname += ".sub" 213 | 214 | if not quiet: 215 | print(f"writting: {fname}") 216 | 217 | with open(fname, "w", encoding="utf-8") as fd: 218 | fd.write(ret) 219 | 220 | 221 | # SUBGHZ_KEY_FILE_TYPE "Flipper SubGhz Key File" 222 | 223 | hex_set = set("abcdefABCDEF0123456789") 224 | 225 | 226 | def is_hex_str(s): 227 | return set(s).issubset(hex_set) 228 | 229 | 230 | def conv_int(arg): 231 | if arg == 0: 232 | return 0 233 | 234 | if not arg: 235 | return None 236 | 237 | if arg[:2].lower() in ["0b", "0x"]: 238 | return int(arg, 0) 239 | 240 | if arg.isdigit(): 241 | return int(arg) 242 | 243 | if is_hex_str(arg): 244 | return int(arg, 16) 245 | 246 | return None 247 | 248 | 249 | # {"time" : "@1.548164s", "model" : "Secplus-v2", "id" : 1959100928, 250 | # "button_id" : 16, "remote_id" : 1959100928, "fixed" : "70678577664", 251 | # "rolling" : "240124739"} 252 | # 253 | # { "time": "@1.198481s", "model": "Secplus-v2", "id": 1082192868, 254 | # "button_id": 161, "remote_id": 1082192868, "fixed": "692571927524", 255 | # "rolling": "24", "packet_1": "018c3492a980", "packet_2": "4804027ebd80", 256 | # "mod": "ASK", "freq": 310.032, 257 | # "rssi": -0.113, "snr": 25.31, "noise": -25.423 } 258 | 259 | 260 | def grok_json(args): 261 | id_list = {} 262 | with open(args.json_log, "r", encoding="UTF-8") as fd: 263 | js = {} 264 | for line in fd: 265 | try: 266 | js = json.loads(line) 267 | except json.JSONDecodeError as _e: 268 | if _debug: 269 | print(_e.msg) 270 | print(_e.doc) 271 | continue 272 | 273 | fid = js.get("fixed", None) 274 | rol = js.get("rolling", None) 275 | 276 | if fid is None or rol is None: 277 | continue 278 | 279 | id_list[fid] = js 280 | 281 | if _debug: 282 | pprint.pprint(id_list) 283 | 284 | for p in id_list.values(): 285 | fr = p.get("freq", None) 286 | if fr: 287 | fr = int((fr // 1) * 1000000) 288 | 289 | p_rolling = int(p["rolling"]) 290 | p_fixed = int(p["fixed"]) 291 | print_file( 292 | p_rolling, p_fixed, fname=args.outfname, quiet=args.quiet, rf_freq=fr 293 | ) 294 | 295 | 296 | def main(): 297 | rolling_out = fixed_out = 0 298 | 299 | args, _extra = arg_opts() 300 | 301 | if args.json_log: 302 | grok_json(args) 303 | return 304 | 305 | if _debug: 306 | print("args", args) 307 | print("_extra", _extra) 308 | 309 | if args.input_file: 310 | b_pkt1 = b_key = None 311 | with open(args.input_file, "r", encoding="UTF-8") as fd: 312 | xx, yy = read_file(fd) 313 | 314 | if xx: 315 | f_key = "".join(xx) 316 | b_key = f"{int(f_key, 16):08b}" 317 | 318 | if yy: 319 | f_pkt1 = "".join(yy) 320 | b_pkt1 = f"{int(f_pkt1, 16):08b}" 321 | 322 | if _debug: 323 | print(f_pkt1, " : ", f_key) 324 | print(b_pkt1[6:], " : ", b_key[6:]) 325 | print(len(b_pkt1[6:]), len(b_key[6:])) 326 | 327 | full_pkt = b_pkt1[6:] + b_key[6:] 328 | 329 | if _debug: 330 | print("full =", full_pkt) 331 | 332 | full_pkt_list = list(map(int, list(full_pkt))) 333 | rolling_out, fixed_out, _data = Secplus.decode_v2(full_pkt_list) 334 | 335 | if _debug: # and (fixed_out or rolling_out): 336 | print( 337 | f">> rolling_out {rolling_out:12d} " 338 | f"{rolling_out:010X} {rolling_out:016b}" 339 | ) 340 | print(f">> fixed_out {fixed_out:12d} " f"{fixed_out:010X} {fixed_out:040b}") 341 | pretty_out = Secplus.pretty_v2(rolling_out, fixed_out) 342 | print(">>", pretty_out) 343 | 344 | a_fixed = args.fixed or fixed_out 345 | # a_rolling = args.rolling or rolling_out 346 | 347 | r_button = args.button or (a_fixed >> 32) or 91 # 8 bits 348 | r_button &= 0xFF 349 | 350 | r_id = args.id or a_fixed or random.randint(2**23, 2**31) # 32 bits id 351 | r_id &= 0xFFFFFFFF 352 | 353 | r_rolling = (args.rolling or rolling_out or 1) & 0x0FFFFFFF # 28 bits max 354 | 355 | fixed_code = (r_id & 0xFFFFFFFF) | (r_button << 32) 356 | 357 | # button_code = random.randint(3, 172) | 0x01 358 | 359 | if _debug: 360 | print(f"r_button {r_button:12d} {r_button:10X} {r_button:>08b}") 361 | print(f"r_id {r_id:12d} {r_id:010X} {r_id:016b}") 362 | print(f"r_rolling {r_rolling:12d} {r_rolling:010X} {r_rolling:016b}") 363 | print( 364 | f"fixed_code {fixed_code:12d} {fixed_code:010X} {fixed_code:040b}" 365 | ) # noqa 366 | 367 | if not args.quiet: 368 | pretty_out = Secplus.pretty_v2(r_rolling, fixed_code) 369 | print(f"\n{pretty_out}\n") 370 | 371 | # only save to file if new of changed 372 | if (args.fixed or args.button or args.id or args.rolling) or not fixed_out: 373 | print_file(r_rolling, fixed_code, args.outfname, args.quiet) 374 | 375 | 376 | if __name__ == "__main__": 377 | main() 378 | -------------------------------------------------------------------------------- /subghz_x10.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Generate X10 RF command in Flipper .sub format 4 | 5 | Peter Shipley github.com/evilpete 6 | 7 | From pkg https://github.com/evilpete/flipper_toolbox 8 | """ 9 | 10 | import sys 11 | import time 12 | 13 | 14 | # Usage; 15 | # ./subghz_x10.py [On|Off] 16 | # Example: 17 | # ./subghz_x10.py B10 on 18 | # 19 | # ./subghz_x10.py B10 Dim Dim Dim 20 | # 21 | # or 22 | # 23 | # Generate a ALL-OFF / ALL-ON for all housecodes 24 | # ./subghz_x10.py -b 25 | # 26 | 27 | # https://www.laser.com/dhouston/rf.html 28 | 29 | 30 | _debug = 0 31 | 32 | # Freq is 310MHz in North America 33 | # 433.92MHz in Europe 34 | 35 | rf_freq = 310000000 36 | 37 | houseCodes = { 38 | "A": 0x60, # 01100000 39 | "B": 0x70, # 01110000 40 | "C": 0x40, # 01000000 41 | "D": 0x50, # 01010000 42 | "E": 0x80, # 10000000 43 | "F": 0x90, # 10010000 44 | "G": 0xA0, # 10100000 45 | "H": 0xB0, # 10110000 46 | "I": 0xE0, # 11100000 47 | "J": 0xF0, # 11110000 48 | "K": 0xC0, # 11000000 49 | "L": 0xD0, # 11010000 50 | "M": 0x00, # 00000000 51 | "N": 0x10, # 00010000 52 | "O": 0x20, # 00100000 53 | "P": 0x30, # 00110000 54 | } 55 | 56 | unit_code = { 57 | 0: 0x0000, # 00000000 00000000 58 | 1: 0x0000, # 00000000 00000000 59 | 2: 0x0010, # 00000000 00010000 60 | 3: 0x0008, # 00000000 00001000 61 | 4: 0x0018, # 00000000 00011000 62 | 5: 0x0040, # 00000000 01000000 63 | 6: 0x0050, # 00000000 01010000 64 | 7: 0x0048, # 00000000 01001000 65 | 8: 0x0058, # 00000000 01011000 66 | 9: 0x0400, # 00000100 00000000 67 | 10: 0x0410, # 00000100 00010000 68 | 11: 0x0408, # 00000100 00001000 69 | 12: 0x0400, # 00000100 00000000 70 | 13: 0x0440, # 00000100 01000000 71 | 14: 0x0450, # 00000100 01010000 72 | 15: 0x0448, # 00000100 01001000 73 | 16: 0x0458, # 00000100 01011000 74 | } 75 | 76 | cmd_code = { 77 | "ON": 0x00, # 00000000 78 | "OFF": 0x20, # 00100000 79 | "BRT": 0x88, # 10001000 80 | "DIM": 0x98, # 10011000 81 | "ALL-OFF": 0x80, # 10000000 82 | "ALL-ON": 0x91, # 10010001 83 | "ALL-LTS-OFF": 0x84, # 10000100 84 | "ALL-LTS-ON": 0x94, # 10010100 85 | # All lights on 0x90 10010000 86 | # All lights off 0xA0 10100000 87 | # All units off 0x80 10000000 88 | } 89 | 90 | 91 | # takes args for housecode unit and x10 command 92 | # and returns a string representing bits for X10 RF command 93 | # 94 | # Args: A 10 ON 95 | # returns 01100100100110110001000011101111 96 | # 97 | def gen_x10(targ_house, targ_unit, targ_cmd): 98 | res = [0, 0] 99 | 100 | if _debug: 101 | print(targ_house, targ_unit, targ_cmd, file=sys.stderr) 102 | 103 | res[0] = houseCodes[targ_house] 104 | 105 | if targ_unit and not cmd_code[targ_cmd] & 0x80: 106 | res[0] |= (unit_code[targ_unit] >> 8) & 0xFF 107 | res[1] |= unit_code[targ_unit] & 0xFF 108 | 109 | res[1] |= cmd_code[targ_cmd] & 0xFF 110 | 111 | if _debug: 112 | print( 113 | f"{res[0]:08b} {res[0]^0xff:08b} {res[1]:08b} {res[1]^0xff:08b}", 114 | file=sys.stderr, 115 | ) 116 | 117 | return f"{res[0]:08b}{res[0]^0xff:08b}{res[1]:08b}{res[1]^0xff:08b}" 118 | 119 | 120 | # Takes a string representing binary bits 121 | # and generates Flipper SubGhz RAW File data 122 | def gen_subfile(pkt_bits, note="x10 command", repeat=1): 123 | datalines = [] 124 | for bits in pkt_bits: 125 | data = [9000, -4500] 126 | 127 | for bit in bits: 128 | if bit == "1": 129 | data.extend((562, -1688)) 130 | else: 131 | data.extend((562, -563)) 132 | 133 | data.extend((562, -40000)) 134 | 135 | for i in range(0, len(data), 510): 136 | batch = map(str, data[i: i + 510]) 137 | datalines.append(f'RAW_Data: {" ".join(batch)}') 138 | 139 | bb = pkt_bits[0] 140 | bin_dat = " ".join([bb[i: i + 8] for i in range(0, len(bb), 8)]) 141 | 142 | hdr = f"""Filetype: Flipper SubGhz RAW File 143 | Version: 1 144 | # {note} {bin_dat} 145 | # Generated with subghz_x10.py https://github.com/evilpete/flipper_toolbox 146 | # {time.ctime()} 147 | Frequency: {rf_freq} 148 | Preset: FuriHalSubGhzPresetOok650Async 149 | Protocol: RAW 150 | """ 151 | 152 | res = hdr 153 | 154 | res += "\n".join(datalines) + "\n" 155 | if repeat > 1: 156 | for i in range(0, repeat): 157 | res += "\n".join(datalines) + "\n" 158 | 159 | return res 160 | 161 | 162 | # 163 | # Generates fikes for the commands 164 | # ALL-ON ALL-OFF ALL-LTS-ON ALL-LTS-OFF 165 | # for for every housecode 166 | # 167 | def gen_brute_all(): 168 | cmd_off = [] 169 | cmd_on = [] 170 | cmd_lts_off = [] 171 | cmd_lts_on = [] 172 | for h in houseCodes: 173 | xoff = gen_x10(h, "", "ALL-OFF") 174 | cmd_off.append(xoff) # repeat 3 times 175 | cmd_off.append(xoff) 176 | cmd_off.append(xoff) 177 | 178 | xon = gen_x10(h, "", "ALL-ON") 179 | cmd_on.append(xon) # repeat 3 times 180 | cmd_on.append(xon) 181 | cmd_on.append(xon) 182 | 183 | xloff = gen_x10(h, "", "ALL-LTS-OFF") 184 | cmd_lts_off.append(xloff) # repeat 3 times 185 | cmd_lts_off.append(xloff) 186 | cmd_lts_off.append(xloff) 187 | 188 | xlon = gen_x10(h, "", "ALL-LTS-ON") 189 | cmd_lts_on.append(xlon) # repeat 3 times 190 | cmd_lts_on.append(xlon) 191 | cmd_lts_on.append(xlon) 192 | 193 | if _debug > 2: 194 | print("cmd_off", cmd_off) 195 | 196 | filenam = "X10_All-OFF" 197 | xdata = gen_subfile(cmd_off, note=filenam, repeat=1) 198 | with open(filenam + ".sub", "w", encoding="utf-8") as fdd: 199 | print(xdata, file=fdd) 200 | 201 | if _debug > 2: 202 | print("cmd_on", cmd_on) 203 | 204 | filenam = "X10_All-ON" 205 | xdata = gen_subfile(cmd_on, note=filenam, repeat=1) 206 | with open(filenam + ".sub", "w", encoding="utf-8") as fdd: 207 | print(xdata, file=fdd) 208 | 209 | filenam = "X10_All-LIGHTS-OFF" 210 | xdata = gen_subfile(cmd_lts_off, note=filenam, repeat=1) 211 | with open(filenam + ".sub", "w", encoding="utf-8") as fdd: 212 | print(xdata, file=fdd) 213 | 214 | filenam = "X10_All-LIGHTS-ON" 215 | xdata = gen_subfile(cmd_lts_on, note=filenam, repeat=1) 216 | with open(filenam + ".sub", "w", encoding="utf-8") as fdd: 217 | print(xdata, file=fdd) 218 | 219 | 220 | if __name__ == "__main__": 221 | args = sys.argv[1:] 222 | 223 | options = ( 224 | "Valid options:\n" 225 | + "\tsubghz_x10.py [unit] \n" 226 | + "or\n" 227 | + "\tsubghz_x10.py -b" 228 | ) 229 | 230 | if args and args[0][0] == "-": 231 | if args[0] == "-b": 232 | gen_brute_all() 233 | sys.exit() 234 | else: 235 | print("unlknown arg {args[0]}") 236 | print(options) 237 | 238 | if len(args) < 2: 239 | print("requires 2 or more args") 240 | sys.exit() 241 | 242 | node_targ = args.pop(0).upper() 243 | node_house = node_targ[0] 244 | node_unit = node_targ[1:] 245 | node_cmd = args.pop(0).upper() 246 | 247 | if node_cmd in cmd_code: 248 | pass 249 | elif node_cmd == "BRIGHT": 250 | node_cmd = "BRT" 251 | elif node_cmd in ["ALL_OFF", "ALLOFF"]: 252 | node_cmd = "ALL-OFF" 253 | elif node_cmd in ["ALL_ON", "ALLON"]: 254 | node_cmd = "ALL-ON" 255 | else: 256 | print("Unknown command code:", node_cmd) 257 | print("\tValid command are:", " ".join(cmd_code)) 258 | print(options) 259 | sys.exit() 260 | 261 | if not (node_house and node_house in houseCodes): 262 | print("Unknown House code:", node_house) 263 | sys.exit() 264 | 265 | if not node_unit: 266 | node_unit = 0 267 | elif node_unit.isdigit(): 268 | node_unit = int(node_unit) 269 | else: 270 | print("Invalid House unit:", node_unit) 271 | sys.exit() 272 | 273 | if int(node_unit) > 16: 274 | print("Invalid House unit:", node_house) 275 | print("\tValid values are 1 -> 16") 276 | sys.exit() 277 | 278 | # rr = gen_x10(node_house, node_unit, node_cmd) 279 | # pkt_data = f"{rr[0]:08b}{rr[0]^0xff:08b}{rr[1]:08b}{rr[1]^0xff:08b}" 280 | 281 | pkt_data = [] 282 | if node_cmd in ["ON", "OFF"]: 283 | pkt_data.append(gen_x10(node_house, node_unit, node_cmd)) 284 | pkt_data += pkt_data * 3 285 | elif node_cmd in ["BRT", "DIM"]: 286 | if node_unit: 287 | pkt_data.append(gen_x10(node_house, node_unit, "ON")) 288 | pkt_data += pkt_data * 2 289 | pkt_data.append(gen_x10(node_house, None, node_cmd)) 290 | elif node_cmd.startswith("ALL"): 291 | node_unit = None 292 | pkt_data.append(gen_x10(node_house, None, node_cmd)) 293 | pkt_data += pkt_data * 3 294 | else: # should never get here.. 295 | print("Unknown command code:", node_cmd) 296 | 297 | if _debug: 298 | print(node_house, node_unit, node_cmd, file=sys.stderr) 299 | 300 | while args: 301 | node_cmd = args.pop(0).upper() 302 | if node_cmd in ["BRT", "DIM"]: 303 | pkt_data.append(gen_x10(node_house, None, node_cmd)) 304 | else: 305 | print("Skipping unknown command code:", node_cmd) 306 | 307 | if _debug: 308 | print("pkt_data", pkt_data, file=sys.stderr) 309 | 310 | if node_unit: 311 | filen = f"{node_house}{node_unit:02d}_{node_cmd}" 312 | else: 313 | filen = f"{node_house}_{node_cmd}" 314 | 315 | fdata = gen_subfile(pkt_data, note=filen) 316 | 317 | with open(filen + ".sub", "w", encoding="utf-8") as fd: 318 | print(fdata, file=fd) 319 | 320 | sys.exit() 321 | -------------------------------------------------------------------------------- /test_dat/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | test / example data 4 | -------------------------------------------------------------------------------- /test_dat/Raw_Sample.sub: -------------------------------------------------------------------------------- 1 | Filetype: Flipper SubGhz RAW File 2 | Version: 1 3 | Frequency: 433920000 4 | Preset: FuriHalSubGhzPresetCustom 5 | Custom_preset_module: CC1101 6 | Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 7 | Protocol: RAW 8 | RAW_Data: 25749 -66 16643 -66 52101 -66 76071 -66 25167 -100 3523 -68 12817 -100 1683 -66 13137 -68 16063 -66 12073 -66 1225 -68 14891 -66 14073 -100 30201 -100 1331 -66 23949 -134 2093 -66 25929 -66 927 -66 19875 -66 2099 -100 51515 -98 461 -100 36315 -66 24583 -100 1327 -66 3285 -68 62649 -100 5965 -66 33455 -66 14825 -98 4761 -98 3455 -66 11409 -66 35341 -66 17017 -66 5291 -66 5103 -66 85611 -66 11361 -66 12913 -68 62157 -64 3375 -66 15693 -66 1499 -66 5347 -66 53579 -130 14549 -66 3417 -66 21197 -66 40323 -66 73109 -66 863 -66 3703 -100 23373 -66 49749 -66 4099 -66 84557 -66 104659 -98 51665 -66 106069 -66 65621 -66 11589 -100 155751 -98 22469 -66 14561 -66 96457 9 | -------------------------------------------------------------------------------- /test_dat/mf-classic-1k-23AD7C86.json: -------------------------------------------------------------------------------- 1 | { 2 | "Created": "proxmark3", 3 | "FileType": "mfcard", 4 | "Card": { 5 | "UID": "23AD7C86", 6 | "ATQA": "0400", 7 | "SAK": "08" 8 | }, 9 | "blocks": { 10 | "0": "23AD7C86740804006263646566676869", 11 | "1": "0AC08556F8106609A1C3B0BD30ADD9F0", 12 | "2": "52000400000000000000000000000000", 13 | "3": "A1670589B2AFFF078069FFFFFFFFFFFF", 14 | "4": "2200020000000000000000C10000001E", 15 | "5": "2200020000000000000000C10000001E", 16 | "6": "00000000000000000000000000000000", 17 | "7": "2A2C13CC242AFF078069FFFFFFFFFFFF", 18 | "8": "00000000000000000000000000000000", 19 | "9": "00000000000000000000000000000000", 20 | "10": "00000000000000000000000000000000", 21 | "11": "FFFFFFFFFFFFFF078069FFFFFFFFFFFF", 22 | "12": "00000000000000000000000000000000", 23 | "13": "00000000000000000000000000000000", 24 | "14": "00000000000000000000000000000000", 25 | "15": "FFFFFFFFFFFFFF078069FFFFFFFFFFFF", 26 | "16": "A2658DB4203A00A2A2659201203A00F4", 27 | "17": "A2655B9D202D004CA2755C00201F00B2", 28 | "18": "A2655C02202D00B2A2655C07202D00B7", 29 | "19": "A1670589B2AFFF078069FFFFFFFFFFFF", 30 | "20": "A2655D2F202D00E0A265600220DE0067", 31 | "21": "A2656002202D00B6A265619920DE00FF", 32 | "22": "A265619D202D0052A26561AB202D0060", 33 | "23": "A1670589B2AFFF078069FFFFFFFFFFFF", 34 | "24": "A2657D5A202D002BA26581B3202D0088", 35 | "25": "A2651BC820CC00D6A2651C8F20CC009E", 36 | "26": "00000000000000000000000000000000", 37 | "27": "A1670589B2AF00000000FFFFFFFFFFFF", 38 | "28": "00000000000000000000000000000000", 39 | "29": "00000000000000000000000000000000", 40 | "30": "00000000000000000000000000000000", 41 | "31": "A1670589B2AF00000000FFFFFFFFFFFF", 42 | "32": "00000000000000000000000000000000", 43 | "33": "00000000000000000000000000000000", 44 | "34": "00000000000000000000000000000000", 45 | "35": "A1670589B2AF00000000FFFFFFFFFFFF", 46 | "36": "00000000000000000000000000000000", 47 | "37": "00000000000000000000000000000000", 48 | "38": "00000000000000000000000000000000", 49 | "39": "A1670589B2AF00000000FFFFFFFFFFFF", 50 | "40": "00000000000000000000000000000000", 51 | "41": "00000000000000000000000000000000", 52 | "42": "00000000000000000000000000000000", 53 | "43": "A1670589B2AF00000000FFFFFFFFFFFF", 54 | "44": "00000000000000000000000000000000", 55 | "45": "00000000000000000000000000000000", 56 | "46": "00000000000000000000000000000000", 57 | "47": "A1670589B2AF00000000FFFFFFFFFFFF", 58 | "48": "00000000000000000000000000000000", 59 | "49": "00000000000000000000000000000000", 60 | "50": "00000000000000000000000000000000", 61 | "51": "A1670589B2AF00000000FFFFFFFFFFFF", 62 | "52": "00000000000000000000000000000000", 63 | "53": "00000000000000000000000000000000", 64 | "54": "00000000000000000000000000000000", 65 | "55": "A1670589B2AF00000000FFFFFFFFFFFF", 66 | "56": "00000000000000000000000000000000", 67 | "57": "00000000000000000000000000000000", 68 | "58": "00000000000000000000000000000000", 69 | "59": "A1670589B2AF00000000FFFFFFFFFFFF", 70 | "60": "00000000000000000000000000000000", 71 | "61": "00000000000000000000000000000000", 72 | "62": "00000000000000000000000000000000", 73 | "63": "A1670589B2AF0400468EFFFFFFFFFFFF" 74 | }, 75 | "SectorKeys": { 76 | "0": { 77 | "KeyA": "A1670589B2AF", 78 | "KeyB": "FFFFFFFFFFFF", 79 | "AccessConditions": "FF078069", 80 | "AccessConditionsText": { 81 | "block0": "read AB; write AB; increment AB; decrement transfer restore AB", 82 | "block1": "read AB; write AB; increment AB; decrement transfer restore AB", 83 | "block2": "read AB; write AB; increment AB; decrement transfer restore AB", 84 | "block3": "write A by A; read/write ACCESS by A; read/write B by A", 85 | "UserData": "69" 86 | } 87 | }, 88 | "1": { 89 | "KeyA": "2A2C13CC242A", 90 | "KeyB": "FFFFFFFFFFFF", 91 | "AccessConditions": "FF078069", 92 | "AccessConditionsText": { 93 | "block4": "read AB; write AB; increment AB; decrement transfer restore AB", 94 | "block5": "read AB; write AB; increment AB; decrement transfer restore AB", 95 | "block6": "read AB; write AB; increment AB; decrement transfer restore AB", 96 | "block7": "write A by A; read/write ACCESS by A; read/write B by A", 97 | "UserData": "69" 98 | } 99 | }, 100 | "2": { 101 | "KeyA": "FFFFFFFFFFFF", 102 | "KeyB": "FFFFFFFFFFFF", 103 | "AccessConditions": "FF078069", 104 | "AccessConditionsText": { 105 | "block8": "read AB; write AB; increment AB; decrement transfer restore AB", 106 | "block9": "read AB; write AB; increment AB; decrement transfer restore AB", 107 | "block10": "read AB; write AB; increment AB; decrement transfer restore AB", 108 | "block11": "write A by A; read/write ACCESS by A; read/write B by A", 109 | "UserData": "69" 110 | } 111 | }, 112 | "3": { 113 | "KeyA": "FFFFFFFFFFFF", 114 | "KeyB": "FFFFFFFFFFFF", 115 | "AccessConditions": "FF078069", 116 | "AccessConditionsText": { 117 | "block12": "read AB; write AB; increment AB; decrement transfer restore AB", 118 | "block13": "read AB; write AB; increment AB; decrement transfer restore AB", 119 | "block14": "read AB; write AB; increment AB; decrement transfer restore AB", 120 | "block15": "write A by A; read/write ACCESS by A; read/write B by A", 121 | "UserData": "69" 122 | } 123 | }, 124 | "4": { 125 | "KeyA": "A1670589B2AF", 126 | "KeyB": "FFFFFFFFFFFF", 127 | "AccessConditions": "FF078069", 128 | "AccessConditionsText": { 129 | "block16": "read AB; write AB; increment AB; decrement transfer restore AB", 130 | "block17": "read AB; write AB; increment AB; decrement transfer restore AB", 131 | "block18": "read AB; write AB; increment AB; decrement transfer restore AB", 132 | "block19": "write A by A; read/write ACCESS by A; read/write B by A", 133 | "UserData": "69" 134 | } 135 | }, 136 | "5": { 137 | "KeyA": "A1670589B2AF", 138 | "KeyB": "FFFFFFFFFFFF", 139 | "AccessConditions": "FF078069", 140 | "AccessConditionsText": { 141 | "block20": "read AB; write AB; increment AB; decrement transfer restore AB", 142 | "block21": "read AB; write AB; increment AB; decrement transfer restore AB", 143 | "block22": "read AB; write AB; increment AB; decrement transfer restore AB", 144 | "block23": "write A by A; read/write ACCESS by A; read/write B by A", 145 | "UserData": "69" 146 | } 147 | }, 148 | "6": { 149 | "KeyA": "A1670589B2AF", 150 | "KeyB": "FFFFFFFFFFFF", 151 | "AccessConditions": "00000000", 152 | "AccessConditionsText": { 153 | "block24": "read AB; write AB; increment AB; decrement transfer restore AB", 154 | "block25": "read AB; write AB; increment AB; decrement transfer restore AB", 155 | "block26": "read AB; write AB; increment AB; decrement transfer restore AB", 156 | "block27": "read A by A; read ACCESS by A; read/write B by A", 157 | "UserData": "00" 158 | } 159 | }, 160 | "7": { 161 | "KeyA": "A1670589B2AF", 162 | "KeyB": "FFFFFFFFFFFF", 163 | "AccessConditions": "00000000", 164 | "AccessConditionsText": { 165 | "block28": "read AB; write AB; increment AB; decrement transfer restore AB", 166 | "block29": "read AB; write AB; increment AB; decrement transfer restore AB", 167 | "block30": "read AB; write AB; increment AB; decrement transfer restore AB", 168 | "block31": "read A by A; read ACCESS by A; read/write B by A", 169 | "UserData": "00" 170 | } 171 | }, 172 | "8": { 173 | "KeyA": "A1670589B2AF", 174 | "KeyB": "FFFFFFFFFFFF", 175 | "AccessConditions": "00000000", 176 | "AccessConditionsText": { 177 | "block32": "read AB; write AB; increment AB; decrement transfer restore AB", 178 | "block33": "read AB; write AB; increment AB; decrement transfer restore AB", 179 | "block34": "read AB; write AB; increment AB; decrement transfer restore AB", 180 | "block35": "read A by A; read ACCESS by A; read/write B by A", 181 | "UserData": "00" 182 | } 183 | }, 184 | "9": { 185 | "KeyA": "A1670589B2AF", 186 | "KeyB": "FFFFFFFFFFFF", 187 | "AccessConditions": "00000000", 188 | "AccessConditionsText": { 189 | "block36": "read AB; write AB; increment AB; decrement transfer restore AB", 190 | "block37": "read AB; write AB; increment AB; decrement transfer restore AB", 191 | "block38": "read AB; write AB; increment AB; decrement transfer restore AB", 192 | "block39": "read A by A; read ACCESS by A; read/write B by A", 193 | "UserData": "00" 194 | } 195 | }, 196 | "10": { 197 | "KeyA": "A1670589B2AF", 198 | "KeyB": "FFFFFFFFFFFF", 199 | "AccessConditions": "00000000", 200 | "AccessConditionsText": { 201 | "block40": "read AB; write AB; increment AB; decrement transfer restore AB", 202 | "block41": "read AB; write AB; increment AB; decrement transfer restore AB", 203 | "block42": "read AB; write AB; increment AB; decrement transfer restore AB", 204 | "block43": "read A by A; read ACCESS by A; read/write B by A", 205 | "UserData": "00" 206 | } 207 | }, 208 | "11": { 209 | "KeyA": "A1670589B2AF", 210 | "KeyB": "FFFFFFFFFFFF", 211 | "AccessConditions": "00000000", 212 | "AccessConditionsText": { 213 | "block44": "read AB; write AB; increment AB; decrement transfer restore AB", 214 | "block45": "read AB; write AB; increment AB; decrement transfer restore AB", 215 | "block46": "read AB; write AB; increment AB; decrement transfer restore AB", 216 | "block47": "read A by A; read ACCESS by A; read/write B by A", 217 | "UserData": "00" 218 | } 219 | }, 220 | "12": { 221 | "KeyA": "A1670589B2AF", 222 | "KeyB": "FFFFFFFFFFFF", 223 | "AccessConditions": "00000000", 224 | "AccessConditionsText": { 225 | "block48": "read AB; write AB; increment AB; decrement transfer restore AB", 226 | "block49": "read AB; write AB; increment AB; decrement transfer restore AB", 227 | "block50": "read AB; write AB; increment AB; decrement transfer restore AB", 228 | "block51": "read A by A; read ACCESS by A; read/write B by A", 229 | "UserData": "00" 230 | } 231 | }, 232 | "13": { 233 | "KeyA": "A1670589B2AF", 234 | "KeyB": "FFFFFFFFFFFF", 235 | "AccessConditions": "00000000", 236 | "AccessConditionsText": { 237 | "block52": "read AB; write AB; increment AB; decrement transfer restore AB", 238 | "block53": "read AB; write AB; increment AB; decrement transfer restore AB", 239 | "block54": "read AB; write AB; increment AB; decrement transfer restore AB", 240 | "block55": "read A by A; read ACCESS by A; read/write B by A", 241 | "UserData": "00" 242 | } 243 | }, 244 | "14": { 245 | "KeyA": "A1670589B2AF", 246 | "KeyB": "FFFFFFFFFFFF", 247 | "AccessConditions": "00000000", 248 | "AccessConditionsText": { 249 | "block56": "read AB; write AB; increment AB; decrement transfer restore AB", 250 | "block57": "read AB; write AB; increment AB; decrement transfer restore AB", 251 | "block58": "read AB; write AB; increment AB; decrement transfer restore AB", 252 | "block59": "read A by A; read ACCESS by A; read/write B by A", 253 | "UserData": "00" 254 | } 255 | }, 256 | "15": { 257 | "KeyA": "A1670589B2AF", 258 | "KeyB": "FFFFFFFFFFFF", 259 | "AccessConditions": "0400468E", 260 | "AccessConditionsText": { 261 | "block60": "read AB; write AB; increment AB; decrement transfer restore AB", 262 | "block61": "read AB", 263 | "block62": "read B; write B", 264 | "block63": "read A by A; read ACCESS by A; read/write B by A", 265 | "UserData": "8E" 266 | } 267 | } 268 | } 269 | } -------------------------------------------------------------------------------- /test_dat/setting_user: -------------------------------------------------------------------------------- 1 | Filetype: Flipper SubGhz Setting File 2 | Version: 1 3 | 4 | # Add All Standard frequencies 5 | #Add_standard_frequencies: true 6 | 7 | # Default Frequency: used as default for "Read" and "Read Raw" 8 | #Default_frequency: 433920000 9 | 10 | # Frequencies used for "Read", "Read Raw" and "Frequency Analyzer" - they added after default ones if enabled in Add_standard_frequencies 11 | Frequency: 302757000 12 | Frequency: 307000000 13 | Frequency: 307500000 14 | Frequency: 307800000 15 | Frequency: 309000000 16 | Frequency: 312000000 17 | Frequency: 312100000 18 | Frequency: 313850000 19 | Frequency: 314000000 20 | Frequency: 314350000 21 | Frequency: 345000000 22 | Frequency: 348000000 23 | Frequency: 387000000 24 | Frequency: 433220000 25 | Frequency: 433889000 26 | Frequency: 464000000 27 | Frequency: 779000000 28 | Frequency: 928000000 29 | 30 | # Frequencies used for hopping mode (keep this list small or flipper will miss signal) - they added after default ones if enabled in Add_standard_frequencies 31 | #Hopper_frequency: 300000000 32 | Hopper_frequency: 345000000 33 | Hopper_frequency: 434420000 34 | 35 | # Custom preset 36 | # format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register 37 | 38 | #Custom_preset_name: AM_1 39 | #Custom_preset_module: CC1101 40 | #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 41 | 42 | #Custom_preset_name: AM_2 43 | #Custom_preset_module: CC1101 44 | #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 45 | 46 | ## Default presets as examples : 47 | 48 | # PresetOok270Async 49 | Custom_preset_name: AM270 50 | Custom_preset_module: CC1101 51 | Custom_preset_data: 02 0d 03 47 08 32 0b 06 14 00 13 00 12 30 11 32 10 67 18 18 19 18 1d 40 1c 00 1b 03 20 fb 22 11 21 b6 00 00 00 C0 00 00 00 00 00 00 52 | 53 | # PresetOok650Async 54 | Custom_preset_name: AM650 55 | Custom_preset_module: CC1101 56 | Custom_preset_data: 02 0d 03 07 08 32 0b 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1d 91 1c 00 1b 07 20 fb 22 11 21 b6 00 00 00 C0 00 00 00 00 00 00 57 | 58 | # Preset2FSKDev238Async 59 | Custom_preset_name: FM238 60 | Custom_preset_module: CC1101 61 | Custom_preset_data: 02 0d 0b 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 04 18 18 19 16 1d 91 1c 00 1b 07 20 fb 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 62 | 63 | 64 | # Preset2FSKDev476Async 65 | Custom_preset_name: FM476 66 | Custom_preset_module: CC1101 67 | Custom_preset_data: 02 0d 0b 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 47 18 18 19 16 1d 91 1c 00 1b 07 20 fb 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 68 | 69 | 70 | # PresetMSK99_97KbAsync 71 | Custom_preset_name: MSK99_9 72 | Custom_preset_module: CC1101 73 | Custom_preset_data: 02 06 03 07 04 46 05 4c 09 00 06 00 0a 00 08 05 0c 23 0b 06 14 f8 13 22 12 72 11 f8 10 5b 15 47 18 18 19 16 1d b2 1c 00 1b c7 22 10 21 56 1a 1c 29 59 00 00 C0 00 00 00 00 00 00 00 74 | 75 | 76 | # PresetGFSK9_99KbAsync 77 | Custom_preset_name: GFSK9_99 78 | Custom_preset_module: CC1101 79 | Custom_preset_data: 02 06 03 47 08 05 0b 06 04 46 05 4c 09 00 06 00 10 c8 11 93 12 12 15 34 18 18 19 16 1b 43 1c 40 1d 91 20 fb 00 00 C0 00 00 00 00 00 00 00 80 | -------------------------------------------------------------------------------- /url2flipnfc.py: -------------------------------------------------------------------------------- 1 | def procesar_url(url): 2 | """ 3 | Procesa la URL para generar su representación en formato NDEF. 4 | """ 5 | # Mapear protocolos a identificadores 6 | protocols = { 7 | "https://www.": "02", "http://www.": "01", 8 | "https://": "04", "http://": "03", 9 | "tel:": "05", "mailto:": "06" 10 | } 11 | 12 | # Identificar el protocolo y calcular el contenido restante 13 | uri_identifier = next((id for proto, id in protocols.items() if url.startswith(proto)), "00") 14 | resto = url.split("://")[-1] if "://" in url else url 15 | resto_hex = ''.join(format(byte, '02X') for byte in resto.encode('utf-8')) 16 | 17 | # Construir el mensaje NDEF 18 | longitud_resto = len(resto) + 1 19 | return f"D1 01 {longitud_resto:02X} 55 {uri_identifier} {resto_hex}" 20 | 21 | 22 | def dividir_en_filas(hex_string, longitud_fila=32): 23 | """ 24 | Divide una cadena hexadecimal en filas de una longitud fija. 25 | """ 26 | bytes_separados = [hex_string[i:i+2] for i in range(0, len(hex_string), 2)] 27 | return [ 28 | ' '.join(bytes_separados[i:i + (longitud_fila // 2)]) 29 | for i in range(0, len(bytes_separados), longitud_fila // 2) 30 | ] 31 | 32 | 33 | def ajustar_bloques(filas, bloque_inicial=4): 34 | """ 35 | Ajusta las filas para que sean múltiplos de 4, agrega relleno y numera los bloques. 36 | """ 37 | # Rellenar filas individuales para que tengan 16 bytes (32 caracteres) 38 | filas_rellenas = [fila.ljust(47, ' ') + "00 " * ((32 - len(fila.replace(' ', '')) // 2)) for fila in filas] 39 | 40 | # Calcular cuántas filas faltan para completar un múltiplo de 4 41 | filas_faltantes = (4 - len(filas_rellenas) % 4) % 4 42 | filas_rellenas.extend(["00 " * 16] * filas_faltantes) 43 | 44 | # Numerar los bloques 45 | return [f"Block {bloque_inicial + i}: {fila}" for i, fila in enumerate(filas_rellenas)] 46 | 47 | 48 | def generar_archivo_flipper(url, uid="1E 0A 23 3F"): 49 | """ 50 | Genera un archivo compatible con Flipper para una URL dada. 51 | """ 52 | contenido_ndef = procesar_url(url) 53 | mensaje_completo = f"03 {len(contenido_ndef.split()):02X} {contenido_ndef} FE" 54 | filas = dividir_en_filas(mensaje_completo) 55 | bloques_numerados = ajustar_bloques(filas, bloque_inicial=4) 56 | 57 | # Cabecera del archivo 58 | cabecera = f""" 59 | Filetype: Flipper NFC device 60 | Version: 4 61 | Device type: Mifare Classic 62 | UID: {uid} 63 | ATQA: 00 04 64 | SAK: 08 65 | Mifare Classic type: 1K 66 | Data format version: 2 67 | """ 68 | return cabecera + '\n'.join(bloques_numerados) 69 | 70 | 71 | # Ejemplo de uso 72 | url = input("Introduce la URL: ") 73 | archivo_flipper = generar_archivo_flipper(url) 74 | 75 | # Mostrar el contenido generado 76 | print("=== Archivo NFC generado ===") 77 | print(archivo_flipper) 78 | --------------------------------------------------------------------------------