Kotlin, 5857 bytes
{var r=1;it.any{v->r*=2;ifr*=2;if(v>29it>29)r*=3;if(v<25it<25)r=1;r%864<1}}
(-1 Byte by replacing the explicit Parameter v-> with the implicit parameter it)
{var r=1;it.any{v->r*=2;if(v>29)r*=3;if(v<25)r=1;r%864<1}}
(-16 bytes using the any{} operation as seen in the Ruby Solution by G B)
{it.stream().reduce(1){r,v->if(r*25>r*v)1 else(r*if(v<30)2 else 6)%864}<1}
(-1 byte thanks Lynn: replaced r>0&&v<25 with r25>rv)
{it.stream().reduce(1){r,v->if(r>0&&v<25)1 else(r*if(v<30)2 else 6)%864}<1}
This lambda expression takes a List and returns true for a heatwave or false else.
Thanks to the magic number 864 and to Udo Borkowski and Mathis for their ideas.
How does if work? Each sequence of numbers is iterated with an any{} operation starting at the reduce value 1. The reduce is multiplied by 2 and multiplied with 3 (23=6) if the number is greater or equal 30. If a number < 25 is seen the reduce starts again at 1. If the reduce is divisible by 864=22222333 then a heatwave is found, and the result of the modulo operation is 0 which results in a true return value in the inner lambda called from the any{} operation which then stops iterating and returns value of true.